~ndiddy/a65

c6029c22c5f6deff0e51215e298999533b0a08ce — Nathan Misner 4 months ago
initial commit
10 files changed, 3770 insertions(+), 0 deletions(-)

A .gitignore
A A65.DOC
A a65.c
A a65.h
A a65.sln
A a65.vcxproj
A a65.vcxproj.filters
A a65eval.c
A a65util.c
A test65.asm
A  => .gitignore +398 -0
@@ 1,398 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore

# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates

# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs

# Mono auto generated files
mono_crash.*

# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/

# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/

# Visual Studio 2017 auto generated files
Generated\ Files/

# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*

# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml

# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c

# Benchmark Results
BenchmarkDotNet.Artifacts/

# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/

# ASP.NET Scaffolding
ScaffoldingReadMe.txt

# StyleCop
StyleCopReport.xml

# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc

# Chutzpah Test files
_Chutzpah*

# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb

# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap

# Visual Studio Trace Files
*.e2e

# TFS 2012 Local Workspace
$tf/

# Guidance Automation Toolkit
*.gpState

# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user

# TeamCity is a build add-in
_TeamCity*

# DotCover is a Code Coverage Tool
*.dotCover

# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json

# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info

# Visual Studio code coverage results
*.coverage
*.coveragexml

# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*

# MightyMoose
*.mm.*
AutoTest.Net/

# Web workbench (sass)
.sass-cache/

# Installshield output folder
[Ee]xpress/

# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html

# Click-Once directory
publish/

# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj

# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/

# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets

# Microsoft Azure Build Output
csx/
*.build.csdef

# Microsoft Azure Emulator
ecf/
rcf/

# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload

# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/

# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs

# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk

# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/

# RIA/Silverlight projects
Generated_Code/

# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak

# SQL Server files
*.mdf
*.ldf
*.ndf

# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl

# Microsoft Fakes
FakesAssemblies/

# GhostDoc plugin setting file
*.GhostDoc.xml

# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/

# Visual Studio 6 build log
*.plg

# Visual Studio 6 workspace options file
*.opt

# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw

# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp

# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp

# Visual Studio 6 technical files
*.ncb
*.aps

# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions

# Paket dependency manager
.paket/paket.exe
paket-files/

# FAKE - F# Make
.fake/

# CodeRush personal settings
.cr/personal

# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc

# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config

# Tabs Studio
*.tss

# Telerik's JustMock configuration file
*.jmconfig

# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs

# OpenCover UI analysis results
OpenCover/

# Azure Stream Analytics local run output
ASALocalRun/

# MSBuild Binary and Structured Log
*.binlog

# NVidia Nsight GPU debugger configuration file
*.nvuser

# MFractors (Xamarin productivity tool) working folder
.mfractor/

# Local History for Visual Studio
.localhistory/

# Visual Studio History (VSHistory) files
.vshistory/

# BeatPulse healthcheck temp database
healthchecksdb

# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/

# Ionide (cross platform F# VS Code tools) working folder
.ionide/

# Fody - auto-generated XML schema
FodyWeavers.xsd

# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace

# Local History for Visual Studio Code
.history/

# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp

# JetBrains Rider
*.sln.iml

A  => A65.DOC +1264 -0
@@ 1,1264 @@
/*
	HEADER:		CUG129;
	TITLE:		6502/65C02 Cross-Assemblers;
	FILENAME:	A65.DOC;
	VERSION:	0.1;
	DATE:		08/27/1988;

	DESCRIPTION:	"These programs let you use your computer to assemble
			code for the MOS Technology 6502 and Rockwell 65C02
			microprocessors.  The program is written in portable
			C rather than BDS C.  All assembler features are
			supported except relocation, linkage, and macros.";

	KEYWORDS:	Software Development, Assemblers, Cross-Assemblers,
			MOS Technology, Rockwell, 6502, 65C02;

	SYSTEM:		CP/M-80, CP/M-86, HP-UX, MSDOS, PCDOS, QNIX;
	COMPILERS:	Aztec C86, Aztec CII, Eco-C, HP-UX, Microsoft C,
			QNIX C;

	WARNINGS:	"HP-UX is a Berkeley 4.2 UNIX look-alike, so the port
			to UNIX should be trivial.  So far, the program
			compiles under two different MSDOS/PCDOS compilers, so
			the port to other MSDOS/PCDOS compilers should be
			easy.";

	AUTHORS:	William C. Colley III;
*/














                           6502/65C02 Cross-Assemblers


                                   Version 0.1


                    Copyright (c) 1986 William C. Colley, III






                            The manual such as it is.











        Legal Note:    This package may be used for any commercial or 
                       non-commercial purpose.  It may be copied and 
                       distributed freely provided that any fee charged 
                       by the distributor of the copy does not exceed the 
                       sum of:  1) the cost of the media the copy is 
                       written on,  2) any required costs of shipping the 
                       copy, and  3) a nominal handling fee.  Any other 
                       distribution requires the written permission of 
                       the author.  Also, the author's copyright notices 
                       shall not be removed from the program source, the 
                       program object, or the program documentation.














                                Table of Contents

        1.0  How to Use the Cross-Assembler Package ..................  3
        2.0  Format of Cross-Assembler Source Lines ..................  4
             2.1  Labels .............................................  5
             2.2  Numeric Constants ..................................  5
             2.3  String Constants ...................................  6
             2.4  Expressions ........................................  6
        3.0  Machine Opcodes .........................................  7
        4.0  Pseudo Opcodes ..........................................  9
             4.1  Pseudo-ops -- END ..................................  9
             4.2  Pseudo-ops -- EQU .................................. 10
             4.3  Pseudo-ops -- FCB .................................. 10
             4.4  Pseudo-ops -- FCC .................................. 10
             4.5  Pseudo-ops -- FDB .................................. 11
             4.6  Pseudo-ops -- IF, ELSE, ENDI ....................... 11
             4.7  Pseudo-ops -- INCL ................................. 12
             4.8  Pseudo-ops -- ORG .................................. 12
             4.9  Pseudo-ops -- PAGE ................................. 12
             4.10 Pseudo-ops -- RMB .................................. 13
             4.11 Pseudo-ops -- SET .................................. 13
             4.12 Pseudo-ops -- TITL ................................. 13
        5.0  Assembly Errors ......................................... 13
             5.1  Error * -- Illegal or Missing Statement ............ 14
             5.2  Error ( -- Parenthesis Imbalance ................... 14
             5.3  Error " -- Missing Quotation Mark .................. 14
             5.4  Error A -- Illegal Addressing Mode ................. 14
             5.5  Error B -- Branch Target Too Distant ............... 14
             5.6  Error D -- Illegal Digit ........................... 14
             5.7  Error E -- Illegal Expression ...................... 15
             5.8  Error I -- IF-ENDI Imbalance ....................... 15
             5.9  Error L -- Illegal Label ........................... 15
             5.10 Error M -- Multiply Defined Label .................. 15
             5.11 Error O -- Illegal Opcode .......................... 15
             5.12 Error P -- Phasing Error ........................... 16
             5.13 Error R -- Illegal Register ........................ 16
             5.14 Error S -- Illegal Syntax .......................... 16
             5.15 Error T -- Too Many Arguments ...................... 16
             5.16 Error U -- Undefined Label ......................... 16
             5.17 Error V -- Illegal Value ........................... 16
        6.0  Warning Messages ........................................ 17
             6.1  Warning -- Illegal Option Ignored .................. 17
             6.2  Warning -- -l Option Ignored -- No File Name ....... 17
             6.3  Warning -- -o Option Ignored -- No File Name ....... 17
             6.4  Warning -- Extra Source File Ignored ............... 17
             6.5  Warning -- Extra Listing File Ignored .............. 17
             6.6  Warning -- Extra Object File Ignored ............... 17
        7.0  Fatal Error Messages .................................... 17
             7.1  Fatal Error -- No Source File Specified ............ 17
             7.2  Fatal Error -- Source File Did Not Open ............ 18
             7.3  Fatal Error -- Listing File Did Not Open ........... 18
             7.4  Fatal Error -- Object File Did Not Open ............ 18
             7.5  Fatal Error -- Error Reading Source File ........... 18
             7.6  Fatal Error -- Disk or Directory Full .............. 18
             7.7  Fatal Error -- File Stack Overflow ................. 18



                                        1



             7.8  Fatal Error -- If Stack Overflow ................... 18
             7.9  Fatal Error -- Too Many Symbols .................... 18























































                                        2



        1.0  How to Use the Cross-Assembler Package

             First, the question, "What does a cross-assembler do?" needs 
        to be addressed as there is considerable confusion on this point.  
        A cross-assembler is just like any other assembler except that it 
        runs on some CPU other than the one for which it assembles code.  
        For example, this package assembles 6502 or 65C02 source code 
        into 6502 or 65C02 object code, but it runs on an 8080, a Z-80, 
        an 8088, or whatever other CPU you happen to have a C compiler 
        for.  The reason that cross-assemblers are useful is that you 
        probably already have a CPU with memory, disk drives, a text 
        editor, an operating system, and all sorts of hard-to-build or 
        expensive facilities on hand.  A cross-assembler allows you to 
        use these facilites to develop code for a 6502 or 65C02.

        This program requires one input file (your 6502 or 65C02 source 
        code) and zero to two output files (the listing and the object).  
        The input file MUST be specified, or the assembler will bomb on a 
        fatal error.  The listing and object files are optional.  If no 
        listing file is specified, no listing is generated, and if no 
        object file is specified, no object is generated.  If the object 
        file is specified, the object is written to this file in "Intel 
        hexadecimal" format.

             The command line for the 6502 cross-assembler looks like 
        this:

                  A65 source_file { -l list_file } { -o object_file }

        where the { } indicates that the specified item is optional.

             Some examples are in order:

             a65 test65.asm                          source:   test65.asm
                                                     listing:  none
                                                     object:   none

             a65 test65.asm -l test65.prn            source:   test65.asm
                                                     listing:  test65.prn
                                                     object:   none

             a65 test65.asm -o test65.hex            source:   test65.asm
                                                     listing:  none
                                                     object:   test65.hex

             a65 test65.asm -l test65.prn -o test65.hex
                                                     source:   test65.asm
                                                     listing:  test65.prn
                                                     object:   test65.hex

             The order in which the source, listing, and object files are 
        specified does not matter.  Note that no default file name exten-
        sions are supplied by the assembler as this gives rise to porta-
        bility problems.




                                        3



             The 65C02 cross assembler works the same way except that the 
        command is "a65c" instead of "a65".


        2.0  Format of Cross-Assembler Source Lines

             The source file that the cross-assembler processes into a 
        listing and an object is an ASCII text file that you can prepare 
        with whatever editor you have at hand.  The most-significant 
        (parity) bit of each character is cleared as the character is 
        read from disk by the cross-assembler, so editors that set this 
        bit (such as WordStar's document mode) should not bother this 
        program.  All printing characters, the ASCII TAB character ($09), 
        and newline character(s) are processed by the assembler.  All 
        other characters are passed through to the listing file, but are 
        otherwise ignored.

             The source file is divided into lines by newline char-
        acter(s).  The internal buffers of the cross-assembler will 
        accommodate lines of up to 255 characters which should be more 
        than ample for almost any job.  If you must use longer lines, 
        change the constant MAXLINE in file A65.H or A65C.H and recompile 
        the cross-assembler.  Otherwise, you will overflow the buffers, 
        and the program will mysteriously crash.

             Each source line is made up of three fields:  the label 
        field, the opcode field, and the argument field.  The label field 
        is optional, but if it is present, it must begin in column 1.  
        The opcode field is optional, but if it is present, it must not 
        begin in column 1.  If both a label and an opcode are present, 
        one or more spaces and/or TAB characters must separate the two.  
        If the opcode requires arguments, they are placed in the argument 
        field which is separated from the opcode field by one or more 
        spaces and/or TAB characters.  Finally, an optional comment can 
        be added to the end of the line.  This comment must begin with a 
        semicolon which signals the assembler to pass the rest of the 
        line to the listing and otherwise ignore it.  Thus, the source 
        line looks like this:

             {label}{ opcode{ arguments}}{;commentary}

        where the { } indicates that the specified item is optional.

             Some examples are in order:

          column 1
             |
             v
             GRONK   LDA   OFFSET, X       ; This line has everything.
                     STA   MAILBOX         ; This line has no label.
             BEEP                          ; This line has no opcode.
             ; This line has no label and no opcode.

             ; The previous line has nothing at all.
                     END                   ; This line has no argument.



                                        4



        2.1  Labels

             A label is any sequence of alphabetic or numeric characters 
        starting with an alphabetic.  The legal alphabetics are:

                    ! & , . : ? [ \ ] ^ _  ` { | }  ~  A-Z  a-z

        The numeric characters are the digits 0-9.  Note that "A" is not 
        the same as "a" in a label.  This can explain mysterious U 
        (undefined label) errors occurring when a label appears to be 
        defined.

             A label is permitted on any line except a line where the 
        opcode is IF, ELSE, or ENDIF.  The label is assigned the value of 
        the assembly program counter before any of the rest of the line 
        is processed except when the opcode is EQU, ORG, or SET.

             Labels can have the same name as opcodes, but they cannot
        have the same name as operators or registers.  The reserved 
        (operator and register) names are:

             A         AND       EQ        GE        GT        HIGH
             LE        LT        LOW       MOD       NE        NOT
             OR        SHL       SHR       X         XOR       Y

             If a label is used in an expression before it is assigned a 
        value, the label is said to be "forward-referenced."  For 
        example:

             L1   EQU  L2 + 1   ; L2 is forward-referenced here.
             L2
             L3   EQU  L2 + 1   ; L2 is not forward-referenced here.


        2.2  Numeric Constants

             Numeric constants can be formed in two ways:  the Intel 
        convention or the Motorola convention.  The cross-assembler 
        supports both.

             An Intel-type numeric constant starts with a numeric 
        character (0-9), continues with zero or more digits (0-9, A-F), 
        and ends with an optional base designator.  The base designators 
        are H for hexadecimal, none or D for decimal, O or Q for octal, 
        and B for binary.  The hex digits a-f are converted to upper case 
        by the assembler.  Note that an Intel-type numeric constant 
        cannot begin with A-F as it would be indistinguishable from a 
        label.  Thus, all of the following evaluate to 255 (decimal):

                   0ffH   255   255D   377O   377Q   11111111B

             A Motorola-type numeric constant starts with a base 
        designator and continues with a string of one or more digits.  
        The base designators are $ for hexadecimal, none for decimal, @ 
        for octal, and % for binary.  As with Intel-type numeric 



                                        5



        constants, a-f are converted to upper case by the assembler.  
        Thus, all of the following evaluate to 255 (decimal):

                          $ff   255   @377   %11111111

             If a numeric constant has a value that is too large to fit 
        into a 16-bit word, it will be truncated on the left to make it 
        fit.  Thus, for example, $123456 is truncated to $3456.


        2.3  String Constants

             A string constant is zero or more characters enclosed in 
        either single quotes (' ') or double quotes (" ").  Single quotes 
        only match single quotes, and double quotes only match double 
        quotes, so if you want to put a single quote in a string, you can 
        do it like this:  "'".  In all contexts except the FCC statement, 
        the first character or two of the string constant are all that 
        are used.  The rest is ignored.  Noting that the ASCII codes for 
        "A" and "B" are $41 and $42, respectively, will explain the 
        following examples:

                  "" and ''           evaluate to $0000
                  "A" and 'A'         evaluate to $0041
                  "AB"                evaluates to $4142

        Note that the null string "" is legal and evaluates to $0000.


        2.4  Expressions

             An expression is made up of labels, numeric constants, and 
        string constants glued together with arithmetic operators, 
        logical operators, and parentheses in the usual way that 
        algebraic expressions are made.  Operators have the following 
        fairly natural order of precedence:

             Highest        anything in parentheses
                            unary +, unary -
                            *, /, MOD, SHL, SHR
                            binary +, binary -
                            LT, LE, EQ, GE, GT, NE
                            NOT
                            AND
                            OR, XOR
             Lowest         HIGH, LOW

             A few notes about the various operators are in order:

             1)   The remainder operator MOD yields the remainder from 
                  dividing its left operand by its right operand.

             2)   The shifting operators SHL and SHR shift their left 
                  operand to the left or right the number of bits 
                  specified by their right operand.



                                        6




             3)   The relational operators LT, LE, EQ, GE, GT, and NE can 
                  also be written as <, <= or =<, =, >= or =>, and <> or 
                  ><, respectively.  They evaluate to $FFFF if the 
                  statement is true, 0 otherwise.

             4)   The logical opeators NOT, AND, OR, and XOR do bitwise 
                  operations on their operand(s).

             5)   HIGH and LOW extract the high or low byte, of an 
                  expression.

             6)   The special symbol * can be used in place of a label or 
                  constant to represent the value of the program counter 
                  before any of the current line has been processed.

             Some examples are in order at this point:

             2 + 3 * 4                          evaluates to 14
             (2 + 3) * 4                        evaluates to 20
             NOT %11110000 XOR %00001010        evaluates to %00000101
             HIGH $1234 SHL 1                   evaluates to $0024
             @001 EQ 0                          evaluates to 0
             @001 = 2 SHR 1                     evaluates to $FFFF

             All arithmetic is unsigned with overflow from the 16-bit 
        word ignored.  Thus:

             32768 * 2                          evaluates to 0


        3.0  Machine Opcodes

             The opcodes of the 6502 and 65C02 processors are listed in 
        the following table along with the various legal argument field 
        formats.  An X in the table indicates that the argument format is 
        legal with the opcode on both processors.  A * in the table 
        indicates that the argument is legal only on the 65C02 processor.  
        Opcodes that are unique to the 65C02 are flagged with a *.

        Opcode                   Argument Format
                  1    2    3    4    5    6    7    8    9    10   11
        ADC       X         *    X         X    X    X    X
        AND       X         *    X         X    X    X    X
        ASL       X                   X    X
        BBR *                                                       X
        BBS *                                                       X
        BCC            X
        BCS            X
        BEQ            X
        BIT       X              *         *
        BMI            X
        BNE            X
        BPL            X
        BRA *          X



                                        7



        Opcode                   Argument Format
                  1    2    3    4    5    6    7    8    9    10   11
        BRK
        BVC            X
        BVS            X
        CLC
        CLD
        CLI
        CLV
        CMP       X         *    X         X    X    X    X
        CPX       X              X
        CPY       X              X
        DEC       X                   *    X
        INC       X                   *    X
        INX
        INY
        JMP       X         X                        *
        JSR       X
        LDA       X         *    X         X    X    X    X
        LDX       X              X              X
        LDY       X              X         X
        LSR       X                   X    X
        NOP
        ORA       X         *    X         X    X    X    X
        PHA
        PHP
        PHX *
        PHY *
        PLA
        PLP
        PLX *
        PLY *
        RMB *                                                  X
        ROL       X                   X    X
        ROR       X                   X    X
        RTI
        RTS
        SBC       X         *    X         X    X    X    X
        SEC
        SED
        SEI
        SMB *                                                  X
        STA       X         *              X    X    X    X
        STX       X                             X
        STY       X                        X
        STZ *     X                        X
        TAX
        TAY
        TRB *     X
        TSB *     X
        TSX
        TXA
        TXS
        TYA




                                        8



             The argument field formats are:

             1)   expression          where expression arbitrary is 
                                      (except RMB and SMB where 
                                      expression is 0 thru 255)

             2)   expression          where the difference between 
                                      expression and the beginning of the 
                                      next instruction must be -128 thru 
                                      127

             3)   (expression)        where expression is arbitrary

             4)   #expression         where expression is -128 thru 255

             5)   A                   Note that the A may be attached to 
                                      the opcode.  Thus "LSR A" and 
                                      "LSRA" are the same.

             6)   expression, X       where expression is arbitrary 
                                      (except STY where expression is 0 
                                      thru 255)

             7)   expression, Y       where expression is 0 thru 255 
                                      (except LDX and STX where 
                                      expression is arbitrary)

             8)   (expression, X)     where expression is 0 thru 255 
                                      (except JMP where expression is 
                                      arbitrary)

             9)   (expression), Y     where expression is 0 thru 255

             10)  bit, expression     where bit is 0 thru 7 and 
                                      expression is 0 thru 255

             11)  bit, expr1, expr2   where bit is 0 thru 7, expr1 is 0 
                                      thru 255, and expr2 follows the 
                                      same rule as 2) above


        4.0  Pseudo Opcodes

             Unlike 6502/65C02 opcodes, pseudo opcodes (pseudo ops) do not 
        represent machine instructions.  They are, rather, directives to 
        the assembler.  These directives require various numbers and 
        types of arguments.  They will be listed individually below.


        4.1  Pseudo-ops -- END

             The END pseudo-op tells the assembler that the source 
        program is over.  Any further lines of the source file are 
        ignored and not passed on to the listing.  If an argument is 
        added to the END statement, the value of the argument will be 



                                        9



        placed in the execution address slot in the Intel hex object 
        file.  The execution address defaults to the program counter 
        value at the point where the END was encountered.  Thus, to 
        specify that the program starts at label START, the END statement 
        would be:

                       END       START

             If end-of-file is encountered on the source file before an 
        END statement is reached, the assembler will add an END statement 
        to the listing and flag it with a * (missing statement) error.


        4.2  Pseudo-ops -- EQU

             The EQU pseudo-op is used to assign a specific value to a 
        label, thus the label on this line is REQUIRED.  Once the value 
        is assigned, it cannot be reassigned by writing the label in 
        column 1, by another EQU statement, or by a SET statement.  Thus, 
        for example, the following statement assigns the value 2 to the 
        label TWO:

             TWO       EQU       1 + 1

             The expression in the argument field must contain no forward 
        references.


        4.3  Pseudo-ops -- FCB

             The FCB (Form Constant Bytes) pseudo-op allows arbitrary 
        bytes to be spliced into the object code.  Its argument is a 
        chain of zero or more expressions that evaluate to -128 thru 255 
        separated by commas.  If a comma occurs with no preceding 
        expression, a $00 byte is spliced into the object code.  The 
        sequence of bytes $FE $FF, $00, $01, $02 could be spliced into 
        the code with the following statement:

                       FCB       -2, -1, , 1, 2


        4.4  Pseudo-ops -- FCC

             The FCC (Form Constant Characters) pseudo-op allows 
        character strings to be spliced into the object code.  Its 
        argument is a chain of zero or more string constants separated by 
        blanks, tabs, or commas.  If a comma occurs with no preceding 
        string constant, an S (syntax) error results.  The string 
        contants are not truncated to two bytes, but are instead copied 
        verbatim into the object code.  Null strings result in no bytes 
        of code.  The message "Kaboom!!" could be spliced into the code 
        with the following statement:

                       FCC       "Kaboom!!"     ;This is 8 bytes of code.




                                       10




        4.5  Pseudo-ops -- FDB

             The FDB (Form Double Bytes) pseudo-op allows 16-bit words to 
        be spliced into the object code.  Its argument is a chain of zero 
        or more expressions separated by commas.  If a comma occurs with 
        no preceding expression, a word of $0000 is spliced into the 
        code.  The word is placed into memory low byte in low address, 
        high byte in high address as per standard MOS Technology order.  
        The sequence of bytes $FE $FF $00 $00 $01 $02 could be spliced 
        into the code with the following statement:

                       FDB       $FFFE, , $0201


        4.6  Pseudo-ops -- IF, ELSE, ENDI

             These three pseudo-ops allow the assembler to choose whether 
        or not to assemble certain blocks of code based on the result of 
        an expression.  Code that is not assembled is passed through to 
        the listing but otherwise ignored by the assembler.  The IF 
        pseudo-op signals the beginning of a conditionally assembled 
        block.  It requires one argument that may contain no forward 
        references.  If the value of the argument is non-zero, the block 
        is assembled.  Otherwise, the block is ignored.  The ENDI pseudo-
        op signals the end of the conditionally assembled block.  For 
        example:

                       IF   EXPRESSION     ;This whole thing generates
                       FCB  $01, $02, $03  ;  no code whatsoever if
                       ENDI                ;  EXPRESSION is zero.

        The ELSE pseudo-op allows the assembly of either one of two 
        blocks, but not both.  The following two sequences are 
        equivalent:

                       IF   EXPRESSION
                       ... some stuff ...
                       ELSE
                       ... some more stuff ...
                       ENDI

             TEMP_LAB  SET  EXPRESSION
                       IF   TEMP_LAB NE 0
                       ... some stuff ...
                       ENDI
                       IF   TEMP_LAB EQ 0
                       ... some more stuff ...
                       ENDI

             The pseudo-ops in this group do NOT permit labels to exist 
        on the same line as the status of the label (ignored or not) 
        would be ambiguous.




                                       11



             All IF statements (even those in ignored conditionally 
        assembled blocks) must have corresponding ENDI statements and all 
        ELSE and ENDI statements must have a corresponding IF statement.

             IF blocks can be nested up to 16 levels deep before the 
        assembler dies of a fatal error.  This should be adequate for any 
        conceivable job, but if you need more, change the constant 
        IFDEPTH in file A65.H and recompile the assembler.


        4.7  Pseudo-ops -- INCL

             The INCL pseudo-op is used to splice the contents of another 
        file into the current file at assembly time.  The name of the 
        file to be INCLuded is specified as a normal string constant, so 
        the following line would splice the contents of file "const.def" 
        into the source code stream:

                       INCL      "const.def"

             INCLuded files may, in turn, INCLude other files until four 
        files are open simultaneously.  This limit should be enough for 
        any conceivable job, but if you need more, change the constant 
        FILES in file A65.H or A65C.H and recompile the assembler.  


        4.8  Pseudo-ops -- ORG

             The ORG pseudo-op is used to set the assembly program 
        counter to a particular value.  The expression that defines this 
        value may contain no forward references.  The default initial 
        value of the assembly program counter is $0000.  The following 
        statement would change the assembly program counter to $F000:

                       ORG       $F000

             If a label is present on the same line as an ORG statement, 
        it is assigned the new value of the assembly program counter.


        4.9  Pseudo-ops -- PAGE

             The PAGE pseudo-op always causes an immediate page ejection 
        in the listing by inserting a form feed ('\f') character before 
        the next line.  If an argument is specified, the argument 
        expression specifies the number of lines per page in the listing.  
        Legal values for the expression are any number except 1 and 2.  A 
        value of 0 turns the listing pagination off.  Thus, the following 
        statement cause a page ejection and would divide the listing into 
        60-line pages:

                       PAGE      60





                                       12



        4.10 Pseudo-ops -- RMB

             The RMB (Reserve Memory Bytes) pseudo-op is used to reserve 
        a block of storage for program variables, or whatever.  This 
        storage is not initialized in any way, so its value at run time 
        will usually be random.  The argument expression (which may
        contain no forward references) is added to the assembly program 
        counter.  The following statement would reserve 10 bytes of 
        storage called "STORAGE":

             STORAGE   RMB       10


        4.11 Pseudo-ops -- SET

             The SET pseudo-op functions like the EQU pseudo-op except 
        that the SET statement can reassign the value of a label that has 
        already been assigned by another SET statement.  Like the EQU 
        statement, the argument expression may contain no forward 
        references.  A label defined by a SET statement cannot be 
        redefined by writing it in column 1 or with an EQU statement.  
        The following series of statements would set the value of label 
        "COUNT" to 1, 2, then 3:

             COUNT     SET       1
             COUNT     SET       2
             COUNT     SET       3


        4.12 Pseudo-ops -- TITL

             The TITL pseudo-op sets the running title for the listing.  
        The argument field is required and must be a string constant, 
        though the null string ("") is legal.  This title is printed 
        after every page ejection in the listing, therefore, if page 
        ejections have not been forced by the PAGE pseudo-op, the title 
        will never be printed.  The following statement would print the 
        title "Random Bug Generator -- Ver 3.14159" at the top of every 
        page of the listing:

                       TITL      "Random Bug Generator -- Ver 3.14159"


        5.0  Assembly Errors

             When a source line contains an illegal construct, the line 
        is flagged in the listing with a single-letter code describing 
        the error.  The meaning of each code is listed below.  In 
        addition, a count of the number of lines with errors is kept and 
        printed on the C "stderr" device (by default, the console) after 
        the END statement is processed.  If more than one error occurs in 
        a given line, only the first is reported.  For example, the 
        illegal label "=$#*'(" would generate the following listing line:

             L  0000   FF 00 00      =$#*'(     CPX       #0



                                       13





        5.1  Error * -- Illegal or Missing Statement

             This error occurs when either:

             1)   the assembler reaches the end of the source file 
                  without seeing an END statement, or

             2)   an END statement is encountered in an INCLude file.

             If you are "sure" that the END statement is present when the 
        assembler thinks that it is missing, it probably is in the 
        ignored section of an IF block.  If the END statement is missing, 
        supply it.  If the END statement is in an INCLude file, delete 
        it.


        5.2  Error ( -- Parenthesis Imbalance

             For every left parenthesis, there must be a right paren-
        thesis.  Count them.


        5.3  Error " -- Missing Quotation Mark

             Strings have to begin and end with either " or '.  Remember 
        that " only matches " while ' only matches '.


        5.4  Error A -- Illegal Addressing Mode

             This error occurs if an addressing mode is specified in the 
        argument field that is not legal with the opcode in the opcode 
        field.  See the table in section 3.0 for the list of legal 
        combinations.


        5.5  Error B -- Branch Target Too Distant

             The 6502 relative branch instructions will only reach -128 
        to +127 bytes from the first byte of the instruction following 
        the branch instruction.  If this error occurs, the source code 
        will have to be rearranged to shorten the distance to the branch 
        target address or a long branch instruction that will reach 
        anywhere (JMP) will have to be used.


        5.6  Error D -- Illegal Digit

             This error occurs if a digit greater than or equal to the 
        base of a numeric constant is found.  For example, a 2 in a 
        binary number would cause a D error.  Especially, watch for 8 or 
        9 in an octal number.




                                       14




        5.7  Error E -- Illegal Expression

             This error occurs because of:

             1)   a missing expression where one is required

             2)   a unary operator used as a binary operator or vice-
                  versa

             3)   a missing binary operator

             4)   a SHL or SHR count that is not 0 thru 15


        5.8  Error I -- IF-ENDI Imbalance

             For every IF there must be a corresponding ENDI.  If this 
        error occurs on an ELSE or ENDI statement, the corresponding IF 
        is missing.  If this error occurs on an END statement, one or 
        more ENDI statements are missing.


        5.9  Error L -- Illegal Label

             This error occurs because of:

             1)   a non-alphabetic in column 1

             2)   a reserved word used as a label

             3)   a missing label on an EQU or SET statement

             4)   a label on an IF, ELSE, or ENDI statement


        5.10 Error M -- Multiply Defined Label

             This error occurs because of:

             1)   a label defined in column 1 or with the EQU statement 
                  being redefined

             2)   a label defined by a SET statement being redefined 
                  either in column 1 or with the EQU statement

             3)   the value of the label changing between assembly passes


        5.11 Error O -- Illegal Opcode

             The opcode field of a source line may contain only a valid 
        machine opcode, a valid pseudo-op, or nothing at all.  Anything 
        else causes this error.




                                       15




        5.12 Error P -- Phasing Error

             This error occurs because of:

             1)   a forward reference in a EQU, ORG, RMB, or SET 
                  statement

             2)   a label disappearing between assembly passes


        5.13 Error R -- Illegal Register

             This error occurs either when the register designator A or B 
        is used with a machine opcode that does not permit it, or when 
        the register designator is missing with a machine opcode that 
        requires it.


        5.14 Error S -- Illegal Syntax

             This error means that an argument field is scrambled.  Sort 
        the mess out and reassemble.


        5.15 Error T -- Too Many Arguments

             This error occurs if there are more items (expressions, 
        register designators, etc.) in the argument field than the opcode 
        or pseudo-op requires.  The assembler ignores the extra items but 
        issues this error in case something is really mangled.


        5.16 Error U -- Undefined Label

             This error occurs if a label is referenced in an expression 
        but not defined anywhere in the source program.  If you are 
        "sure" you have defined the label, note that upper and lower case 
        letters in labels are different.  Defining "LABEL" does not 
        define "Label."


        5.17 Error V -- Illegal Value

             This error occurs because:

             1)   an index offset is not 0 thru 255, or

             2)   an 8-bit immediate value is not -128 thru 255, or

             3)   an FCB argument is not -128 thru 255, or

             4)   an INCL argument refers to a file that does not exist.





                                       16



        6.0  Warning Messages

             Some errors that occur during the parsing of the cross-
        assembler command line are non-fatal.  The cross-assembler flags 
        these with a message on the C "stdout" device (by default, the 
        console) beginning with the word "Warning."  The messages are 
        listed below:


        6.1  Warning -- Illegal Option Ignored

             The only options that the cross-assembler knows are -l and  
        -o.  Any other command line argument beginning with - will draw 
        this error.


        6.2  Warning -- -l Option Ignored -- No File Name
        6.3  Warning -- -o Option Ignored -- No File Name

             The -l and -o options require a file name to tell the 
        assembler where to put the listing file or object file.  If this 
        file name is missing, the option is ignored.


        6.4  Warning -- Extra Source File Ignored

             The cross-assembler will only assemble one file at a time, 
        so source file names after the first are ignored.  To assemble a 
        second file, invoke the assembler again.  Note that under CP/M-
        80, the old trick of reexecuting a core image will NOT work as 
        the initialized data areas are not reinitialized prior to the 
        second run.


        6.5  Warning -- Extra Listing File Ignored
        6.6  Warning -- Extra Object File Ignored

             The cross-assembler will only generate one listing and one 
        object file per assembly run, so -l and -o options after the 
        first are ignored.


        7.0  Fatal Error Messages

             Several errors that occur during the parsing of the cross-
        assembler command line or during the assembly run are fatal.  The 
        cross-assembler flags these with a message on the C "stdout" 
        device (by default, the console) beginning with the words "Fatal 
        Error."  The messages are explained below:


        7.1  Fatal Error -- No Source File Specified

             This one is self-explanatory.  The assembler does not know 
        what to assemble.



                                       17





        7.2  Fatal Error -- Source File Did Not Open

             The assembler could not open the source file.  The most 
        likely cause is that the source file as specified on the command 
        line does not exist.  On larger systems, there could also be 
        priviledge violations.  Rarely, a read error in the disk 
        directory could cause this error.


        7.3  Fatal Error -- Listing File Did Not Open
        7.4  Fatal Error -- Object File Did Not Open

             This error indicates either a defective listing or object 
        file name or a full disk directory.  Correct the file name or 
        make more room on the disk.


        7.5  Fatal Error -- Error Reading Source File

             This error generally indicates a read error in the disk data 
        space.  Use your backup copy of the source file (You do have one, 
        don't you?) to recreate the mangled file and reassemble.


        7.6  Fatal Error -- Disk or Directory Full

             This one is self-explanatory.  Some more space must be found 
        either by deleting files or by using a disk with more room on it.


        7.7  Fatal Error -- File Stack Overflow

             This error occurs if you exceed the INCLude file limit of 
        four files open simultaneously.  This limit can be increased by 
        increasing the constant FILES in file A65.H or A65C.H and 
        recompiling the cross-assembler.


        7.8  Fatal Error -- If Stack Overflow

             This error occurs if you exceed the nesting limit of 16 IF 
        blocks.  This limit can be increased by increasing the constant 
        IFDEPTH in file A65.H or A65C.H and recompiling the cross-
        assembler.


        7.9  Fatal Error -- Too Many Symbols

             Congratulations!  You have run out of memory.  The space for 
        the cross-assembler's symbol table is allocated at run-time using 
        the C library function alloc(), so the cross-assembler will use 
        all available memory.  The only solutions to this problem are to 
        lessen the number of labels in the source program, to use a 



                                       18



        larger memory model (MSDOS/PCDOS systems only), or to add more 
        memory to your machine.























































                                       19

\ No newline at end of file

A  => a65.c +495 -0
@@ 1,495 @@
/*
	HEADER:		CUG219;
	TITLE:		6502 Cross-Assembler (Portable);
	FILENAME:	A65.C;
	VERSION:	0.1;
	DATE:		08/27/1988;

	DESCRIPTION:	"This program lets you use your computer to assemble
			code for the MOS Technology 6502 microprocessors.  The
			program is written in portable C rather than BDS C.
			All assembler features are supported except relocation
			linkage, and macros.";

	KEYWORDS:	Software Development, Assemblers, Cross-Assemblers,
			MOS Technology, 6502;

	SYSTEM:		CP/M-80, CP/M-86, HP-UX, MSDOS, PCDOS, QNIX;
	COMPILERS:	Aztec C86, Aztec CII, CI-C86, Eco-C, Eco-C88, HP-UX,
			Lattice C, Microsoft C,	QNIX C;

	WARNINGS:	"This program is written in as portable C as possible.
			A port to BDS C would be extremely difficult, but see
			volume CUG113.  A port to Toolworks C is untried."

	AUTHORS:	William C. Colley III;
*/

/*
		      6502 Cross-Assembler in Portable C

		   Copyright (c) 1986 William C. Colley, III

Revision History:

Ver	Date		Description

0.0	NOV 1986	Derived from my 6800/6801 cross-assembler.  WCC3.

0.1	AUG 1988	Fixed a bug in the command line parser that puts it
			into a VERY long loop if the user types a command line
			like "A65 FILE.ASM -L".  WCC3 per Alex Cameron.

This file contains the main program and line assembly routines for the
assembler.  The main program parses the command line, feeds the source lines to
the line assembly routine, and sends the results to the listing and object file
output routines.  It also coordinates the activities of everything.  The line
assembly routines uses the expression analyzer and the lexical analyzer to
parse the source line and convert it into the object bytes that it represents.
*/

/*  Get global goodies:  */

#include "a65.h"

/*  Define global mailboxes for all modules:				*/

char errcode, line[MAXLINE + 1], title[MAXLINE];
int pass = 0;
int eject, filesp, forwd, listhex;
unsigned address, argattr, bytes, errors, listleft, obj[MAXLINE], pagelen, pc;
FILE *filestk[FILES], *source;
TOKEN token;

/*  Mainline routine.  This routine parses the command line, sets up	*/
/*  the assembler at the beginning of each pass, feeds the source text	*/
/*  to the line assembler, feeds the result to the listing and hex file	*/
/*  drivers, and cleans everything up at the end of the run.		*/

static int done, ifsp, off;

void main(int argc, char **argv) {
    SCRATCH unsigned *o;
    int newline();
    void asm_line();
    void lclose(), lopen(), lputs();
    void hclose(), hopen(), hputc();
    void error(), fatal_error(), warning();

    printf("6502 Cross-Assembler (Portable) Ver 0.1\n");
    printf("Copyright (c) 1986 William C. Colley, III\n\n");

    while (--argc > 0) {
		if (**++argv == '-') {
			switch (toupper(*++*argv)) {
			case 'L':   
				if (!*++*argv) {
					if (!--argc) { warning(NOLST);  break; }
					else ++argv;
				}
				lopen(*argv);
				break;

			case 'O':
				if (!*++*argv) {
					if (!--argc) { warning(NOHEX);  break; }
					else ++argv;
				}
				hopen(*argv);
				break;

			default:
				warning(BADOPT);
			}
		}
		else if (filestk[0]) warning(TWOASM);
		else if (!(filestk[0] = fopen(*argv,"r"))) fatal_error(ASMOPEN);
    }
    if (!filestk[0]) fatal_error(NOASM);

    while (++pass < 3) {
		fseek(source = filestk[0],0L,0);  done = off = FALSE;
		errors = filesp = ifsp = pagelen = pc = 0;  title[0] = '\0';
		while (!done) {
			errcode = ' ';
			if (newline()) {
				error('*');
				strcpy(line,"\tEND\n");
				done = eject = TRUE;  listhex = FALSE;
				bytes = 0;
			}
			else asm_line();
			pc = word(pc + bytes);
			if (pass == 2) {
				lputs();
				for (o = obj; bytes--; hputc(*o++));
			}
		}
    }

    fclose(filestk[0]);  lclose();  hclose();

    if (errors) printf("%d Error(s)\n",errors);
    else printf("No Errors\n");

    exit(errors);
}

/*  Line assembly routine.  This routine gets the contents of the	*/
/*  argument field from the source file using the expression evaluator	*/
/*  and lexical analyzer.  It makes all validity checks on the		*/
/*  arguments validity, fills a buffer with the machine code bytes and	*/
/*  returns nothing.							*/

static char label[MAXLINE];
static int ifstack[IFDEPTH] = { ON };

static OPCODE *opcod;

void asm_line() {
    SCRATCH int i;
    int isalph(), popc();
    OPCODE *find_code(), *find_operator();
    void do_label(), flush(), normal_op(), pseudo_op();
    void error(), pops(), pushc(), trash();

    address = pc;  bytes = 0;  eject = forwd = listhex = FALSE;
    for (i = 0; i < BIGINST; obj[i++] = NOP);

    label[0] = '\0';
    if ((i = popc()) != ' ' && i != '\n') {
		if (isalph(i)) {
			pushc(i);  pops(label);
			if (find_operator(label)) { label[0] = '\0';  error('L'); }
		}
		else {
			error('L');
			while ((i = popc()) != ' ' && i != '\n');
		}
    }

    trash();  opcod = NULL;
    if ((i = popc()) != '\n') {
		if (!isalph(i)) error('S');
		else {
			pushc(i);  pops(token.sval);
			if (!(opcod = find_code(token.sval))) error('O');
		}
		if (!opcod) { listhex = TRUE;  bytes = BIGINST; }
    }

    if (opcod && opcod -> attr & ISIF) { if (label[0]) error('L'); }
    else if (off) { listhex = FALSE;  flush();  return; }

    if (!opcod) { do_label();  flush(); }
    else {
		listhex = TRUE;
		if (opcod -> attr & PSEUDO) pseudo_op();
		else normal_op();
		while ((i = popc()) != '\n') if (i != ' ') error('T');
    }
    source = filestk[filesp];
    return;
}

static void flush() {
    while (popc() != '\n');
}

static void do_label() {
    SCRATCH SYMBOL *l;
    SYMBOL *find_symbol(), *new_symbol();
    void error();

    if (label[0]) {
		listhex = TRUE;
		if (pass == 1) {
			if (!((l = new_symbol(label)) -> attr)) {
				l -> attr = FORWD + VAL;
				l -> valu = pc;
			}
		}
		else {
			if (l = find_symbol(label)) {
				l -> attr = VAL;
				if (l -> valu != pc) error('M');
			}
			else error('P');
		}
    }
}

static void normal_op() {
    SCRATCH unsigned opcode, operand;
    unsigned do_args();
    void do_label(), error();

    opcode = opcod -> valu;  bytes = BIGINST;
    do_label();  operand = do_args();
    switch (opcod -> attr) {
	case CPXY:  
		if (argattr & ARGIMM) goto do_immediate;
		opcode += 0x04;

	case BITOP: 
		if (argattr != ARGNUM) { error('A');  return; }
	    goto do_zero_page;

	case INHOP:
		if (argattr) error('T');
	    bytes = 1;  break;

	case JUMP:  
		if (argattr == (ARGIND + ARGNUM)) {
			opcode += 0x20;  break;
		}

	case CALL:  
		if (argattr != ARGNUM) { error('A');  return; }
		break;

	case LOGOP: 
		if (!(argattr & ARGA)) goto do_inc_op;
	    opcode += 0x04;  bytes = 1;  break;

	case RELBR: 
		if (argattr != ARGNUM) { error('A');  return; }
	    bytes = 2;  operand -= pc + 2;
		if (clamp(operand) > 0x007f && operand < 0xff80) {
			error('B');  operand = 0xfffe;
		}
		break;

	case STXY:  
		if ((argattr & (opcode == 0x86 ? ~ARGY : ~ARGX)) != ARGNUM) { 
			error ('A');
			return;
		}
		if (argattr & (ARGX + ARGY)) {
			if (operand > 0x00ff) {	error('V');  operand = 0; }
			opcode += 0x10;  bytes = 2;  break;
		}
		goto do_zero_page;

	case TWOOP: 
		if (!(argattr & ARGNUM)) { error('A');  return; }
		if (argattr & ARGIMM) {
			if (opcode == 0x81) { error('A');  return; }
			opcode += 0x08;  goto do_immediate;
		}
		if (argattr & ARGIND) {
			if (argattr & ARGY) opcode += 0x10;
			else if (!(argattr & ARGX)) { error('A');  return; }
			if (operand > 0x00ff) { error('V');  operand = 0; }
			bytes = 2;
		}
		else if (argattr & ARGY) opcode += 0x18;
		else { opcode += 0x04;  goto do_indexed_x; }
		break;

	case LDXY:
		if (!(argattr & ARGIMM)) {
			if (opcode == 0xa2 && (argattr & ARGY))
				argattr ^= (ARGX + ARGY);
			opcode += 0x04;  goto do_inc_op;
		}
do_immediate:
		if (operand > 0x00ff && operand < 0xff80) {
			error('V');  operand = 0;
		}
		bytes = 2;  break;

do_inc_op:
	case INCOP: 
		if ((argattr & ~ARGX) != ARGNUM) { error('A');  return; }
do_indexed_x:
		if (argattr & ARGX) opcode += 0x10;
do_zero_page:
		if (!forwd && operand <= 0x00ff) bytes = 2;
		else opcode += 0x08;
		break;
    }
    obj[2] = high(operand);  obj[1] = low(operand);  obj[0] = opcode;
    return;
}

static void pseudo_op() {
    SCRATCH char *s;
    SCRATCH unsigned *o, u;
    SCRATCH SYMBOL *l;
    unsigned expr();
    SYMBOL *find_symbol(), *new_symbol();
    TOKEN *lex();
    void do_label(), error(), fatal_error(), hseek(), unlex();

    o = obj;
    switch (opcod -> valu) {
	case ELSE:  
		listhex = FALSE;
		if (ifsp) off = (ifstack[ifsp] = -ifstack[ifsp]) != ON;
		else error('I');
		break;

	case END:   
		do_label();
		if (filesp) { listhex = FALSE;  error('*'); }
		else {
			done = eject = TRUE;
			if (pass == 2 && (lex() -> attr & TYPE) != EOL) {
				unlex();  hseek(address = expr());
			}
			if (ifsp) error('I');
		}
		break;

	case ENDI:  
		listhex = FALSE;
		if (ifsp) off = ifstack[--ifsp] != ON;
		else error('I');
		break;

	case EQU:   
		if (label[0]) {
			if (pass == 1) {
				if (!((l = new_symbol(label)) -> attr)) {
					l -> attr = FORWD + VAL;
					address = expr();
					if (!forwd) l -> valu = address;
				}
			}
			else {
				if (l = find_symbol(label)) {
					l -> attr = VAL;
					address = expr();
					if (forwd) error('P');
					if (l -> valu != address) error('M');
				}
				else error('P');
			}
		}
		else error('L');
		break;

	case FCB:   
		do_label();
		do {
			if ((lex() -> attr & TYPE) == SEP) u = 0;
			else {
				unlex();
				if ((u = expr()) > 0xff && u < 0xff80) {
					u = 0;  error('V');
				}
			}
			*o++ = low(u);  ++bytes;
		} while ((token.attr & TYPE) == SEP);
		break;

	case FCC:   
		do_label();
		while ((lex() -> attr & TYPE) != EOL) {
			if ((token.attr & TYPE) == STR) {
				for (s = token.sval; *s; *o++ = *s++)
					++bytes;
				if ((lex() -> attr & TYPE) != SEP) unlex();
			}
			else error('S');
		}
		break;

	case FDB:   
		do_label();
		do {
			if ((lex() -> attr & TYPE) == SEP) u = 0;
			else { unlex();  u = expr(); }
			*o++ = low(u);  *o++ = high(u);
			bytes += 2;
		} while ((token.attr & TYPE) == SEP);
		break;

	case IF:   
		if (++ifsp == IFDEPTH) fatal_error(IFOFLOW);
		address = expr();
		if (forwd) { error('P');  address = TRUE; }
		if (off) { listhex = FALSE;  ifstack[ifsp] = NULL; }
		else {
			ifstack[ifsp] = address ? ON : OFF;
			if (!address) off = TRUE;
		}
		break;

	case INCL:  
		listhex = FALSE;  do_label();
		if ((lex() -> attr & TYPE) == STR) {
			if (++filesp == FILES) fatal_error(FLOFLOW);
			if (!(filestk[filesp] = fopen(token.sval,"r"))) {
				--filesp;  error('V');
			}
		}
		else error('S');
		break;

	case ORG:   
		u = expr();
		if (forwd) error('P');
		else {
			pc = address = u;
			if (pass == 2) hseek(pc);
		}
		do_label();
		break;

	case PAGE:  
		listhex = FALSE;  do_label();
		if ((lex() -> attr & TYPE) != EOL) {
			unlex();  pagelen = expr();
			if (pagelen > 0 && pagelen < 3) {
				pagelen = 0;  error('V');
			}
		}
		eject = TRUE;
		break;

	case RMB:   
		do_label();
		u = word(pc + expr());
		if (forwd) error('P');
		else {
			pc = u;
			if (pass == 2) hseek(pc);
		}
		break;

	case SET:   
		if (label[0]) {
			if (pass == 1) {
				if (!((l = new_symbol(label)) -> attr) || (l -> attr & SOFT)) {
					l -> attr = FORWD + SOFT + VAL;
					address = expr();
					if (!forwd) l -> valu = address;
				}
			}
			else {
				if (l = find_symbol(label)) {
					address = expr();
					if (forwd) error('P');
					else if (l -> attr & SOFT) {
						l -> attr = SOFT + VAL;
						l -> valu = address;
					}
					else error('M');
				}
				else error('P');
			}
		}
		else error('L');
		break;

	case TITL:  
		listhex = FALSE;  do_label();
		if ((lex() -> attr & TYPE) == EOL) title[0] = '\0';
		else if ((token.attr & TYPE) != STR) error('S');
		else strcpy(title,token.sval);
		break;
    }
    return;
}

A  => a65.h +239 -0
@@ 1,239 @@
/*
	HEADER:		CUG219;
	TITLE:		6502 Cross-Assembler (Portable);
	FILENAME:	A65.H;
	VERSION:	0.1;
	DATE:		08/27/1988;

	DESCRIPTION:	"This program lets you use your computer to assemble
			code for the MOS Technology 6502 microprocessors.  The
			program is written in portable C rather than BDS C.
			All assembler features are supported except relocation
			linkage, and macros.";

	KEYWORDS:	Software Development, Assemblers, Cross-Assemblers,
			MOS Technology, 6502;

	SYSTEM:		CP/M-80, CP/M-86, HP-UX, MSDOS, PCDOS, QNIX;
	COMPILERS:	Aztec C86, Aztec CII, CI-C86, Eco-C, Eco-C88, HP-UX,
			Lattice C, Microsoft C,	QNIX C;

	WARNINGS:	"This program is written in as portable C as possible.
			A port to BDS C would be extremely difficult, but see
			volume CUG113.  A port to Toolworks C is untried."

	AUTHORS:	William C. Colley III;
*/

/*
		      6502 Cross-Assembler in Portable C

		   Copyright (c) 1986 William C. Colley, III

Revision History:

Ver	Date		Description

0.0	NOV 1986	Derived from my 6800/6801 cross-assembler.  WCC3.

0.1	AUG 1988	Fixed a bug in the command line parser that puts it
			into a VERY long loop if the user types a command line
			like "A65 FILE.ASM -L".  WCC3 per Alex Cameron.

This header file contains the global constants and data type definitions for
all modules of the cross-assembler.
*/

#include <stdio.h>

/*  Boolean defines  */
#define TRUE (1)
#define FALSE (0)


/*  On 8-bit machines, the static type is as efficient as the register	*/
/*  type and far more efficient than the auto type.  On larger machines	*/
/*  such as the 8086 family, this is not necessarily the case.  To	*/
/*  let you experiment to see what generates the fastest, smallest code	*/
/*  for your machine, I have declared internal scratch variables in	*/
/*  functions "SCRATCH int", "SCRATCH unsigned", etc.  A SCRATCH	*/
/*  varible is made static below, but you might want to try register	*/
/*  instead.								*/

#define	SCRATCH		static

/*  A slow, but portable way of cracking an unsigned into its various	*/
/*  component parts:							*/

#define	clamp(u)	((u) &= 0xffff)
#define	high(u)		(((u) >> 8) & 0xff)
#define	low(u)		((u) & 0xff)
#define	word(u)		((u) & 0xffff)

/*  The longest source line the assembler can hold without exploding:	*/

#define	MAXLINE		255

/*  The maximum number of source files that can be open simultaneously:	*/

#define	FILES		4

/*  The fatal error messages generated by the assembler:		*/

#define	ASMOPEN		"Source File Did Not Open"
#define	ASMREAD		"Error Reading Source File"
#define	DSKFULL		"Disk or Directory Full"
#define	FLOFLOW		"File Stack Overflow"
#define	HEXOPEN		"Object File Did Not Open"
#define	IFOFLOW		"If Stack Overflow"
#define	LSTOPEN		"Listing File Did Not Open"
#define	NOASM		"No Source File Specified"
#define	SYMBOLS		"Too Many Symbols"

/*  The warning messages generated by the assembler:			*/

#define	BADOPT		"Illegal Option Ignored"
#define	NOHEX		"-o Option Ignored -- No File Name"
#define	NOLST		"-l Option Ignored -- No File Name"
#define	TWOASM		"Extra Source File Ignored"
#define	TWOHEX		"Extra Object File Ignored"
#define	TWOLST		"Extra Listing File Ignored"

/*  Line assembler (A65.C) constants:					*/

#define	BIGINST		3		/*  longest instruction length	*/
#define	IFDEPTH		16		/*  maximum IF nesting level	*/
#define	NOP		0xea		/*  processor's NOP opcode	*/
#define	ON		1		/*  assembly turned on		*/
#define	OFF		-1		/*  assembly turned off		*/

/*  Line assembler (A65.C) opcode attribute word flag masks:		*/

#define	PSEUDO		0x80	/*  is pseudo op			*/
#define	ISIF		0x40	/*  is IF, ELSE, or ENDIF		*/

/*  Line assembler (A65.C) pseudo-op opcode token values:		*/

#define	ELSE		1
#define	END		2
#define	ENDI		3
#define	EQU		4
#define	FCB		5
#define	FCC		6
#define	FDB		7
#define	IF		8
#define	INCL		9
#define	ORG		10
#define	PAGE		11
#define	RMB		12
#define	SET		13
#define	TITL		14

/*  Line assembler (A65.C) machine opcode attribute values:		*/

#define	CPXY		0
#define	BITOP		1
#define	INHOP		2
#define	JUMP		3
#define	CALL		4
#define	LOGOP		5
#define	RELBR		6
#define	STXY		7
#define	TWOOP		8
#define	LDXY		9
#define	INCOP		10

/*  Opcode argument field parser (A65EVAL.C) attribute bit masks:	*/

#define	ARGA		040	/*  A register specified.		*/
#define	ARGX		020	/*  X register specified.		*/
#define	ARGY		010	/*  Y register specified.		*/
#define	ARGIND		004	/*  Argument field in parentheses.	*/
#define	ARGIMM		002	/*  Immediate addressing specified.	*/
#define	ARGNUM		001	/*  Argument value present.		*/

/*  Lexical analyzer (A65EVAL.C) token buffer and stream pointer:	*/

typedef struct {
    unsigned attr;
    unsigned valu;
    char sval[MAXLINE + 1];
} TOKEN;

/*  Lexical analyzer (A65EVAL.C) token attribute values:		*/

#define	EOL		0	/*  end of line				*/
#define	SEP		1	/*  field separator			*/
#define	OPR		2	/*  operator				*/
#define	STR		3	/*  character string			*/
#define	VAL		4	/*  value				*/
#define	IMM		5	/*  immediate designator		*/
#define	REG		6	/*  register designator			*/

/*  Lexical analyzer (A65EVAL.C) token attribute word flag masks:	*/

#define	BINARY		0x8000	/*  Operator:	is binary operator	*/
#define	UNARY		0x4000	/*		is unary operator	*/
#define	PREC		0x0f00	/*		precedence		*/

#define	FORWD		0x8000	/*  Value:	is forward referenced	*/
#define	SOFT		0x4000	/*		is redefinable		*/

#define	TYPE		0x000f	/*  All:	token type		*/

/*  Lexical analyzer (A65EVAL.C) operator token values (unlisted ones	*/
/*  use ASCII characters):						*/

#define	AND		0
#define	GE		1
#define	HIGH		2
#define	LE		3
#define	LOW		4
#define	MOD		5
#define	NE		6
#define	NOT		7
#define	OR		8
#define	SHR		9
#define	SHL		10
#define	XOR		11

/*  Lexical analyzer (A65EVAL.C) operator precedence values:		*/

#define	UOP1		0x0000	/*  unary +, unary -			*/
#define	MULT		0x0100	/*  *, /, MOD, SHL, SHR			*/
#define	ADDIT		0x0200	/*  binary +, binary -			*/
#define	RELAT		0x0300	/*  >, >=, =, <=, <, <>			*/
#define	UOP2		0x0400	/*  NOT					*/
#define	LOG1		0x0500	/*  AND					*/
#define	LOG2		0x0600	/*  OR, XOR				*/
#define	UOP3		0x0700	/*  HIGH, LOW				*/
#define	START2		0x0800	/*  beginning of subexpression		*/
#define	RPREN		0x0900	/*  )					*/
#define	LPREN		0x0a00	/*  (					*/
#define	ENDEX		0x0b00	/*  end of expression			*/
#define	START		0x0c00	/*  beginning of expression		*/

/*  Utility package (A65UTIL.C) symbol table routines:			*/

struct _symbol {
    unsigned attr;
    unsigned valu;
    struct _symbol *left, *right;
    char sname[1];
};

typedef struct _symbol SYMBOL;

#define	SYMCOLS		4

/*  Utility package (A65UTIL.C) opcode/operator table routines:		*/

typedef struct {
    unsigned attr;
    unsigned valu;
    char oname[5];
} OPCODE;

/*  Utility package (A65UTIL.C) hex file output routines:		*/

#define	HEXSIZE		32

A  => a65.sln +31 -0
@@ 1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.4.33205.214
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "a65", "a65.vcxproj", "{9B211D7E-3BBA-4483-A0B7-36FC2DB24243}"
EndProject
Global
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		Debug|x64 = Debug|x64
		Debug|x86 = Debug|x86
		Release|x64 = Release|x64
		Release|x86 = Release|x86
	EndGlobalSection
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
		{9B211D7E-3BBA-4483-A0B7-36FC2DB24243}.Debug|x64.ActiveCfg = Debug|x64
		{9B211D7E-3BBA-4483-A0B7-36FC2DB24243}.Debug|x64.Build.0 = Debug|x64
		{9B211D7E-3BBA-4483-A0B7-36FC2DB24243}.Debug|x86.ActiveCfg = Debug|Win32
		{9B211D7E-3BBA-4483-A0B7-36FC2DB24243}.Debug|x86.Build.0 = Debug|Win32
		{9B211D7E-3BBA-4483-A0B7-36FC2DB24243}.Release|x64.ActiveCfg = Release|x64
		{9B211D7E-3BBA-4483-A0B7-36FC2DB24243}.Release|x64.Build.0 = Release|x64
		{9B211D7E-3BBA-4483-A0B7-36FC2DB24243}.Release|x86.ActiveCfg = Release|Win32
		{9B211D7E-3BBA-4483-A0B7-36FC2DB24243}.Release|x86.Build.0 = Release|Win32
	EndGlobalSection
	GlobalSection(SolutionProperties) = preSolution
		HideSolutionNode = FALSE
	EndGlobalSection
	GlobalSection(ExtensibilityGlobals) = postSolution
		SolutionGuid = {2C6BD130-3B81-499C-A928-7584F3B89874}
	EndGlobalSection
EndGlobal

A  => a65.vcxproj +140 -0
@@ 1,140 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup Label="ProjectConfigurations">
    <ProjectConfiguration Include="Debug|Win32">
      <Configuration>Debug</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|Win32">
      <Configuration>Release</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Debug|x64">
      <Configuration>Debug</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|x64">
      <Configuration>Release</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
  </ItemGroup>
  <PropertyGroup Label="Globals">
    <VCProjectVersion>16.0</VCProjectVersion>
    <Keyword>Win32Proj</Keyword>
    <ProjectGuid>{9b211d7e-3bba-4483-a0b7-36fc2db24243}</ProjectGuid>
    <RootNamespace>a65</RootNamespace>
    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
    <ConfigurationType>Application</ConfigurationType>
    <UseDebugLibraries>true</UseDebugLibraries>
    <PlatformToolset>v143</PlatformToolset>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
    <ConfigurationType>Application</ConfigurationType>
    <UseDebugLibraries>false</UseDebugLibraries>
    <PlatformToolset>v143</PlatformToolset>
    <WholeProgramOptimization>true</WholeProgramOptimization>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
    <ConfigurationType>Application</ConfigurationType>
    <UseDebugLibraries>true</UseDebugLibraries>
    <PlatformToolset>v143</PlatformToolset>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
    <ConfigurationType>Application</ConfigurationType>
    <UseDebugLibraries>false</UseDebugLibraries>
    <PlatformToolset>v143</PlatformToolset>
    <WholeProgramOptimization>true</WholeProgramOptimization>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
  <ImportGroup Label="ExtensionSettings">
  </ImportGroup>
  <ImportGroup Label="Shared">
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <PropertyGroup Label="UserMacros" />
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <ClCompile>
      <WarningLevel>Level3</WarningLevel>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
    </ClCompile>
    <Link>
      <SubSystem>Console</SubSystem>
      <GenerateDebugInformation>true</GenerateDebugInformation>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <ClCompile>
      <WarningLevel>Level3</WarningLevel>
      <FunctionLevelLinking>true</FunctionLevelLinking>
      <IntrinsicFunctions>true</IntrinsicFunctions>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
    </ClCompile>
    <Link>
      <SubSystem>Console</SubSystem>
      <EnableCOMDATFolding>true</EnableCOMDATFolding>
      <OptimizeReferences>true</OptimizeReferences>
      <GenerateDebugInformation>true</GenerateDebugInformation>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <ClCompile>
      <WarningLevel>Level3</WarningLevel>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
    </ClCompile>
    <Link>
      <SubSystem>Console</SubSystem>
      <GenerateDebugInformation>true</GenerateDebugInformation>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <ClCompile>
      <WarningLevel>Level3</WarningLevel>
      <FunctionLevelLinking>true</FunctionLevelLinking>
      <IntrinsicFunctions>true</IntrinsicFunctions>
      <SDLCheck>true</SDLCheck>
      <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <ConformanceMode>true</ConformanceMode>
    </ClCompile>
    <Link>
      <SubSystem>Console</SubSystem>
      <EnableCOMDATFolding>true</EnableCOMDATFolding>
      <OptimizeReferences>true</OptimizeReferences>
      <GenerateDebugInformation>true</GenerateDebugInformation>
    </Link>
  </ItemDefinitionGroup>
  <ItemGroup>
    <ClCompile Include="a65.c" />
    <ClCompile Include="a65eval.c" />
    <ClCompile Include="a65util.c" />
  </ItemGroup>
  <ItemGroup>
    <ClInclude Include="a65.h" />
  </ItemGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
  <ImportGroup Label="ExtensionTargets">
  </ImportGroup>
</Project>
\ No newline at end of file

A  => a65.vcxproj.filters +33 -0
@@ 1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <Filter Include="Source Files">
      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
      <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
    </Filter>
    <Filter Include="Header Files">
      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
      <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
    </Filter>
    <Filter Include="Resource Files">
      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
    </Filter>
  </ItemGroup>
  <ItemGroup>
    <ClCompile Include="a65.c">
      <Filter>Source Files</Filter>
    </ClCompile>
    <ClCompile Include="a65eval.c">
      <Filter>Source Files</Filter>
    </ClCompile>
    <ClCompile Include="a65util.c">
      <Filter>Source Files</Filter>
    </ClCompile>
  </ItemGroup>
  <ItemGroup>
    <ClInclude Include="a65.h">
      <Filter>Header Files</Filter>
    </ClInclude>
  </ItemGroup>
</Project>
\ No newline at end of file

A  => a65eval.c +492 -0
@@ 1,492 @@
/*
	HEADER:		CUG219;
	TITLE:		6502 Cross-Assembler (Portable);
	FILENAME:	A65EVAL.C;
	VERSION:	0.1;
	DATE:		08/27/1988;

	DESCRIPTION:	"This program lets you use your computer to assemble
			code for the MOS Technology 6502 microprocessors.  The
			program is written in portable C rather than BDS C.
			All assembler features are supported except relocation
			linkage, and macros.";

	KEYWORDS:	Software Development, Assemblers, Cross-Assemblers,
			MOS Technology, 6502;

	SYSTEM:		CP/M-80, CP/M-86, HP-UX, MSDOS, PCDOS, QNIX;
	COMPILERS:	Aztec C86, Aztec CII, CI-C86, Eco-C, Eco-C88, HP-UX,
			Lattice C, Microsoft C,	QNIX C;

	WARNINGS:	"This program is written in as portable C as possible.
			A port to BDS C would be extremely difficult, but see
			volume CUG113.  A port to Toolworks C is untried."

	AUTHORS:	William C. Colley III;
*/

/*
		      6502 Cross-Assembler in Portable C

		   Copyright (c) 1986 William C. Colley, III

Revision History:

Ver	Date		Description

0.0	NOV 1986	Derived from my 6800/6801 cross-assembler.  WCC3.

0.1	AUG 1988	Fixed a bug in the command line parser that puts it
			into a VERY long loop if the user types a command line
			like "A65 FILE.ASM -L".  WCC3 per Alex Cameron.

This file contains the assembler's expression evaluator and lexical analyzer.
The lexical analyzer chops the input character stream up into discrete tokens
that are processed by the expression analyzer and the line assembler.  The
expression analyzer processes the token stream into unsigned results of
arithmetic expressions.
*/

/*  Get global goodies:  */

#include "a65.h"

/*  Get access to global mailboxes defined in A65.C:			*/

extern char line[];
extern int filesp, forwd, pass;
extern unsigned argattr, pc;
extern FILE *filestk[], *source;
extern TOKEN token;

/*  Machine opcode argument field parsing routine.  The token stream	*/
/*  from the lexical analyzer is processed to extract addressing mode	*/
/*  information and (possibly) an actual address that can be reduced to */
/*  an unsigned value.  If an error occurs during the evaluation, the	*/
/*  global flag forwd is set to indicate to the line assembler that it	*/
/*  should not base certain decisions on the result of the evaluation.	*/
/*  The address is passed back as the return value of the function.	*/
/*  The addressing mode information is passed back through the global	*/
/*  mailbox argattr.							*/

static int bad;

unsigned do_args() {
    SCRATCH int c;
    SCRATCH unsigned u;
    TOKEN *lex();
    int popc();
    unsigned eval(), expr();
    void exp_error(), pushc(), trash(), unlex();

    argattr = ARGNUM;  u = 0;  bad = FALSE;
    switch (lex() -> attr & TYPE) {
	case REG:
		if (token.valu == 'A') argattr = ARGA;
		else exp_error('S');
		break;

	case EOL:   
		argattr = NULL;  
		break;

	case IMM:
		argattr = ARGIMM + ARGNUM;
		return expr();

	case SEP:   
		u = 0;  goto have_number;

	case OPR:
		if (token.valu == '(') {
			bad = FALSE;  u = eval(START2);
			switch (lex() -> attr & TYPE) {
			case EOL:
				exp_error('(');
				return 0;

			case SEP:
				if ((lex() -> attr & TYPE) != REG || token.valu != 'X')
					exp_error('S');
				else if ((lex() -> attr & TYPE) != OPR || token.valu != ')')
					exp_error('(');
				else argattr += (ARGX + ARGIND);
				return bad ? 0 : u;

			case OPR:
				argattr = (ARGIND + ARGNUM);  trash();
				if ((c = popc()) == '\n') return u;
				if (c == ',') {
					if (lex() -> attr & TYPE != REG
					|| token.valu != 'Y')
					exp_error('S');
					else argattr += ARGY;
					return bad ? 0 : u;
				}
				argattr = ARGNUM;  pushc(c);
				token.attr = VAL;  token.valu = u;
			}
		}

	case VAL:
	case STR:   
		unlex();  u = eval(START);
		if ((token.attr & TYPE) != SEP) {
			if ((token.attr & TYPE) != EOL) exp_error('S');
			break;
		}

have_number:
		if ((lex() -> attr & TYPE) != REG || token.valu == 'A')
			exp_error('S');
		    else argattr += (token.valu == 'X' ? ARGX : ARGY);
		    break;
    }
    return bad ? 0 : u;
}

/*  Expression analysis routine.  The token stream from the lexical	*/
/*  analyzer is processed as an arithmetic expression and reduced to an	*/
/*  unsigned value.  If an error occurs during the evaluation, the	*/
/*  global flag	forwd is set to indicate to the line assembler that it	*/
/*  should not base certain decisions on the result of the evaluation.	*/

unsigned expr() {
    SCRATCH unsigned u;
    unsigned eval();

    bad = FALSE;
    u = eval(START);
    return bad ? 0 : u;
}

static unsigned eval(unsigned pre) {
   register unsigned op, u, v;
   TOKEN *lex();
   void exp_error(), unlex();

	for (;;) {
		u = op = lex()->valu;
		switch (token.attr & TYPE) {
		case REG:
		case IMM:   exp_error('S');  break;

		case SEP:	if (pre != START) unlex();
		case EOL:	exp_error('E');  return;

		case OPR:
			if (!(token.attr & UNARY)) { exp_error('E');  break; }
			u = (op == '*' ? pc : eval((op == '+' || op == '-') ? (unsigned)UOP1 : token.attr & PREC));
			switch (op) {
				case '-':   u = word(~u + 1);  break;
				case NOT:   u ^= 0xffff;  break;
				case HIGH:  u = high(u);  break;
				case LOW:   u = low(u);  break;
			}

		case VAL:
		case STR:
			for (;;) {
				op = lex()->valu;
				switch (token.attr & TYPE) {
				case REG:
				case IMM:	exp_error('S');  break;

				case SEP:
					if (pre != START) unlex();
				case EOL:
					if (pre == LPREN) exp_error('(');
					return u;

				case STR:
				case VAL:	exp_error('E');  break;

				case OPR:
					if (!(token.attr & BINARY)) {
						exp_error('E');  break;
					}
					if ((token.attr & PREC) >= pre) {
						unlex();  return u;
					}
					if (op != ')') {
						v = eval(token.attr & PREC);
					}
					switch (op) {
						case '+':   u += v;		break;
						case '-':   u -= v;		break;
						case '*':   u *= v;		break;
						case '/':   u /= v;		break;
						case MOD:   u %= v;		break;
						case AND:   u &= v;		break;
						case OR:    u |= v;		break;
						case XOR:   u ^= v;		break;
						case '<':   u = u < v;	break;
						case LE:    u = u <= v;	break;
						case '=':   u = u == v;	break;
						case GE:    u = u >= v;	break;
						case '>':   u = u > v;  break;
						case NE:    u = u != v;	break;
						case SHL:
							if (v > 15) exp_error('E');
							else u <<= v;
							break;

						case SHR:
							if (v > 15) exp_error('E');
							else u >>= v;
							break;

						case ')':
							if (pre == LPREN) return u;
							exp_error('(');
							break;
					}
					clamp(u);
					break;
				}
			}
			break;
		}
	}
}

static void exp_error(char c) {
    forwd = bad = TRUE;  error(c);
}

/*  Lexical analyzer.  The source input character stream is chopped up	*/
/*  into its component parts and the pieces are evaluated.  Symbols are	*/
/*  looked up, operators are looked up, etc.  Everything gets reduced	*/
/*  to an attribute word, a numeric value, and (possibly) a string	*/
/*  value.								*/

static int oldt = FALSE;
static int quote = FALSE;

TOKEN *lex() {
	SCRATCH char c, *p;
	SCRATCH unsigned b;
	SCRATCH OPCODE *o;
	SCRATCH SYMBOL *s;
	OPCODE *find_operator();
	SYMBOL *find_symbol();
	void exp_error(), make_number(), pops(), pushc(), trash();

	if (oldt) { oldt = FALSE;  return &token; }
	trash();
	if (isalph(c = popc())) {
		pushc(c);  pops(token.sval);
		if (o = find_operator(token.sval)) {
			token.attr = o -> attr;
			token.valu = o -> valu;
		}
		else {
			token.attr = VAL;  token.valu = 0;
			if (s = find_symbol(token.sval)) {
				token.valu = s -> valu;
				if (pass == 2 && s -> attr & FORWD) forwd = TRUE;
			}
			else exp_error('U');
		}
	}
	else if (isnum(c)) {
		pushc(c);  pops(token.sval);
		for (p = token.sval; *p; ++p);
		switch (toupper(*--p)) {
			case 'B':	b = 2;	break;
	
			case 'O':
			case 'Q':	b = 8;	break;
	
			default:	++p;
			case 'D':	b = 10;	break;
	
			case 'H':	b = 16;	break;
		}
		*p = '\0';  make_number(b);
	}
	else switch (c) {
	case '%':   b = 2;  goto num;
	
	case '@':   b = 8;  goto num;
	
	case '$':   b = 16;
num:		    pops(token.sval);
				make_number(b);
				break;

	case '#':   token.attr = IMM;
				break;

	case '(':   token.attr = UNARY + LPREN + OPR;
				goto opr1;

	case ')':   token.attr = BINARY + RPREN + OPR;
				goto opr1;

	case '+':   token.attr = BINARY + UNARY + ADDIT + OPR;
				goto opr1;

	case '-':   token.attr = BINARY + UNARY + ADDIT + OPR;
				goto opr1;

	case '*':   token.attr = BINARY + UNARY + MULT + OPR;
				goto opr1;

	case '/':   token.attr = BINARY + MULT + OPR;
opr1:			token.valu = c;
				break;

	case '<':   token.valu = c;
				if ((c = popc()) == '=') token.valu = LE;
				else if (c == '>') token.valu = NE;
				else pushc(c);
				goto opr2;

	case '=':   token.valu = c;
				if ((c = popc()) == '<') token.valu = LE;
				else if (c == '>') token.valu = GE;
				else pushc(c);
				goto opr2;

	case '>':   token.valu = c;
				if ((c = popc()) == '<') token.valu = NE;
				else if (c == '=') token.valu = GE;
				else pushc(c);
opr2:		    token.attr = BINARY + RELAT + OPR;
				break;

	case '\'':
	case '"':   quote = TRUE;  token.attr = STR;
				for (p = token.sval; (*p = popc()) != c; ++p)
				if (*p == '\n') { exp_error('"');  break; }
				*p = '\0';  quote = FALSE;
				if ((token.valu = token.sval[0]) && token.sval[1])
				token.valu = (token.valu << 8) + token.sval[1];
				break;

	case ',':   token.attr = SEP;
				break;

	case '\n':  token.attr = EOL;
				break;
    }
    return &token;
}

static void make_number(unsigned base) {
    SCRATCH char *p;
    SCRATCH unsigned d;
    void exp_error();

    token.attr = VAL;
    token.valu = 0;
    for (p = token.sval; *p; ++p) {
		d = toupper(*p) - (isnum(*p) ? '0' : 'A' - 10);
		token.valu = token.valu * base + d;
		if (!ishex(*p) || d >= base) { exp_error('D');  break; }
    }
    clamp(token.valu);
    return;
}

int isalph(char c) {
    return (c >= 'A' && c <= '~') || c == '!' ||
		c == '&' || c == '.' || c == ':' || c == '?';
}

static int isnum(char c) {
    return c >= '0' && c <= '9';
}

static int ishex(char c) {
    return isnum(c) || ((c = toupper(c)) >= 'A' && c <= 'F');
}

static int isalnum(char c) {
    return isalph(c) || isnum(c);
}

/*  Push back the current token into the input stream.  One level of	*/
/*  pushback is supported.						*/

void unlex() {
    oldt = TRUE;
    return;
}

/*  Get an alphanumeric string into the string value part of the	*/
/*  current token.  Leading blank space is trashed.			*/

void pops(char *s) {
    void pushc(), trash();

    trash();
    for (; isalnum(*s = popc()); ++s);
    pushc(*s);  *s = '\0';
    return;
}

/*  Trash blank space and push back the character following it.		*/

void trash() {
    SCRATCH char c;
    void pushc();

    while ((c = popc()) == ' ');
    pushc(c);
    return;
}

/*  Get character from input stream.  This routine does a number of	*/
/*  other things while it's passing back characters.  All control	*/
/*  characters except \t and \n are ignored.  \t is mapped into ' '.	*/
/*  Semicolon is mapped to \n.  In addition, a copy of all input is set	*/
/*  up in a line buffer for the benefit of the listing.			*/

static int oldc, eol;
static char *lptr;

int popc() {
    SCRATCH int c;

    if (oldc) { c = oldc;  oldc = '\0';  return c; }
    if (eol) return '\n';
    for (;;) {
		if ((c = getc(source)) != EOF && (c &= 0377) == ';' && !quote) {
			do *lptr++ = c;
			while ((c = getc(source)) != EOF && (c &= 0377) != '\n');
		}
		if (c == EOF) c = '\n';
		if ((*lptr++ = c) >= ' ' && c <= '~') return c;
		if (c == '\n') { eol = TRUE;  *lptr = '\0';  return '\n'; }
		if (c == '\t') return quote ? '\t' : ' ';
    }
}

/*  Push character back onto input stream.  Only one level of push-back	*/
/*  supported.  \0 cannot be pushed back, but nobody would want to.	*/

void pushc(char c) {
    oldc = c;
    return;
}

/*  Begin new line of source input.  This routine returns non-zero if	*/
/*  EOF	has been reached on the main source file, zero otherwise.	*/

int newline() {
    void fatal_error();

    oldc = '\0';  lptr = line;
    oldt = eol = FALSE;
    while (feof(source)) {
		if (ferror(source)) fatal_error(ASMREAD);
		if (filesp) {
			fclose(source);
			source = filestk[--filesp];
		}
		else return TRUE;
    }
    return FALSE;
}

A  => a65util.c +455 -0
@@ 1,455 @@
/*
	HEADER:		CUG219;
	TITLE:		6502 Cross-Assembler (Portable);
	FILENAME:	A65UTIL.C;
	VERSION:	0.1;
	DATE:		08/27/1988;

	DESCRIPTION:	"This program lets you use your computer to assemble
			code for the MOS Technology 6502 microprocessors.  The
			program is written in portable C rather than BDS C.
			All assembler features are supported except relocation
			linkage, and macros.";

	KEYWORDS:	Software Development, Assemblers, Cross-Assemblers,
			MOS Technology, 6502;

	SYSTEM:		CP/M-80, CP/M-86, HP-UX, MSDOS, PCDOS, QNIX;
	COMPILERS:	Aztec C86, Aztec CII, CI-C86, Eco-C, Eco-C88, HP-UX,
			Lattice C, Microsoft C,	QNIX C;

	WARNINGS:	"This program is written in as portable C as possible.
			A port to BDS C would be extremely difficult, but see
			volume CUG113.  A port to Toolworks C is untried."

	AUTHORS:	William C. Colley III;
*/

/*
		      6502 Cross-Assembler in Portable C

		   Copyright (c) 1986 William C. Colley, III

Revision History:

Ver	Date		Description

0.0	NOV 1986	Derived from my 6800/6801 cross-assembler.  WCC3.

0.1	AUG 1988	Fixed a bug in the command line parser that puts it
			into a VERY long loop if the user types a command line
			like "A65 FILE.ASM -L".  WCC3 per Alex Cameron.

This module contains the following utility packages:

	1)  symbol table building and searching

	2)  opcode and operator table searching

	3)  listing file output

	4)  hex file output

	5)  error flagging
*/

/*  Get global goodies:  */

#include "a65.h"

/*  Make sure that MSDOS compilers using the large memory model know	*/
/*  that calloc() returns pointer to char as an MSDOS far pointer is	*/
/*  NOT compatible with the int type as is usually the case.		*/

char *calloc();

/*  Get access to global mailboxes defined in A65.C:			*/

extern char errcode, line[], title[];
extern int eject, listhex;
extern unsigned address, bytes, errors, listleft, obj[], pagelen;

/*  The symbol table is a binary tree of variable-length blocks drawn	*/
/*  from the heap with the calloc() function.  The root pointer lives	*/
/*  here:								*/

static SYMBOL *sroot = NULL;

/*  Add new symbol to symbol table.  Returns pointer to symbol even if	*/
/*  the symbol already exists.  If there's not enough memory to store	*/
/*  the new symbol, a fatal error occurs.				*/

SYMBOL *new_symbol(char *nam) {
    SCRATCH int i;
    SCRATCH SYMBOL **p, *q;
    void fatal_error();

    for (p = &sroot; (q = *p) && (i = strcmp(nam,q -> sname)); )
	p = i < 0 ? &(q -> left) : &(q -> right);
    if (!q) {
		if (!(*p = q = (SYMBOL *)calloc(1,sizeof(SYMBOL) + strlen(nam))))
			fatal_error(SYMBOLS);
		strcpy(q -> sname,nam);
    }
    return q;
}

/*  Look up symbol in symbol table.  Returns pointer to symbol or NULL	*/
/*  if symbol not found.						*/

SYMBOL *find_symbol(char *nam) {
    SCRATCH int i;
    SCRATCH SYMBOL *p;

    for (p = sroot; p && (i = strcmp(nam,p -> sname));
	p = i < 0 ? p -> left : p -> right);
    return p;
}

/*  Opcode table search routine.  This routine pats down the opcode	*/
/*  table for a given opcode and returns either a pointer to it or	*/
/*  NULL if the opcode doesn't exist.					*/

OPCODE *find_code(char *nam) {
    OPCODE *bsearch();

    static OPCODE opctbl[] = {
		{ TWOOP,			0x61,	"ADC"	},
		{ TWOOP,			0x21,	"AND"	},
		{ LOGOP,			0x06,	"ASL"	},
		{ INHOP,			0x0a,	"ASLA"	},
		{ RELBR,			0x90,	"BCC"	},
		{ RELBR,			0xb0,	"BCS"	},
		{ RELBR,			0xf0,	"BEQ"	},
		{ BITOP,			0x24,	"BIT"	},
		{ RELBR,			0x30,	"BMI"	},
		{ RELBR,			0xd0,	"BNE"	},
		{ RELBR,			0x10,	"BPL"	},
		{ INHOP,			0x00,	"BRK"	},
		{ RELBR,			0x50,	"BVC"	},
		{ RELBR,			0x70,	"BVS"	},
		{ INHOP,			0x18,	"CLC"	},
		{ INHOP,			0xd8,	"CLD"	},
		{ INHOP,			0x58,	"CLI"	},
		{ INHOP,			0xb8,	"CLV"	},
		{ TWOOP,			0xc1,	"CMP"	},
		{ CPXY,				0xe0,	"CPX"	},
		{ CPXY,				0xc0,	"CPY"	},
		{ INCOP,			0xc6,	"DEC"	},
		{ INHOP,			0xca,	"DEX"	},
		{ INHOP,			0x88,	"DEY"	},
		{ PSEUDO + ISIF,	ELSE,	"ELSE"	},
		{ PSEUDO,			END,	"END"	},
		{ PSEUDO + ISIF,	ENDI,	"ENDI"	},
		{ TWOOP,			0x41,	"EOR"	},
		{ PSEUDO,			EQU,	"EQU"	},
		{ PSEUDO,			FCB,	"FCB"	},
		{ PSEUDO,			FCC,	"FCC"	},
		{ PSEUDO,			FDB,	"FDB"	},
		{ PSEUDO + ISIF,	IF,		"IF"	},
		{ INCOP,			0xe6,	"INC"	},
		{ PSEUDO,			INCL,	"INCL"	},
		{ INHOP,			0xe8,	"INX"	},
		{ INHOP,			0xc8,	"INY"	},
		{ JUMP,				0x4c,	"JMP"	},
		{ CALL,				0x20,	"JSR"	},
		{ TWOOP,			0xa1,	"LDA"	},
		{ LDXY,				0xa2,	"LDX"	},
		{ LDXY,				0xa0,	"LDY"	},
		{ LOGOP,			0x46,	"LSR"	},
		{ INHOP,			0x4a,	"LSRA"	},
		{ INHOP,			0xea,	"NOP"	},
		{ TWOOP,			0x01,	"ORA"	},
		{ PSEUDO,			ORG,	"ORG"	},
		{ PSEUDO,			PAGE,	"PAGE"	},
		{ INHOP,			0x48,	"PHA"	},
		{ INHOP,			0x08,	"PHP"	},
		{ INHOP,			0x68,	"PLA"	},
		{ INHOP,			0x28,	"PLP"	},
		{ PSEUDO,			RMB,	"RMB"	},
		{ LOGOP,			0x26,	"ROL"	},
		{ INHOP,			0x2a,	"ROLA"	},
		{ LOGOP,			0x66,	"ROR"	},
		{ INHOP,			0x6a,	"RORA"	},
		{ INHOP,			0x40,	"RTI"	},
		{ INHOP,			0x60,	"RTS"	},
		{ TWOOP,			0xe1,	"SBC"	},
		{ INHOP,			0x38,	"SEC"	},
		{ INHOP,			0xf8,	"SED"	},
		{ INHOP,			0x78,	"SEI"	},
		{ PSEUDO,			SET,	"SET"	},
		{ TWOOP,			0x81,	"STA"	},
		{ STXY,				0x86,	"STX"	},
		{ STXY,				0x84,	"STY"	},
		{ INHOP,			0xaa,	"TAX"	},
		{ INHOP,			0xa8,	"TAY"	},
		{ PSEUDO,			TITL,	"TITL"	},
		{ INHOP,			0xba,	"TSX"	},
		{ INHOP,			0x8a,	"TXA"	},
		{ INHOP,			0x9a,	"TXS"	},
		{ INHOP,			0x98,	"TYA"	}
    };

    return bsearch(opctbl,opctbl + (sizeof(opctbl) / sizeof(OPCODE)),nam);
}

/*  Operator table search routine.  This routine pats down the		*/
/*  operator table for a given operator and returns either a pointer	*/
/*  to it or NULL if the opcode doesn't exist.				*/

OPCODE *find_operator(char *nam) {
    OPCODE *bsearch();

    static OPCODE oprtbl[] = {
		{ REG,						'A',		"A"		},
		{ BINARY + LOG1  + OPR,		AND,		"AND"	},
		{ BINARY + RELAT + OPR,		'=',		"EQ"	},
		{ BINARY + RELAT + OPR,		GE,			"GE"	},
		{ BINARY + RELAT + OPR,		'>',		"GT"	},
		{ UNARY  + UOP3  + OPR,		HIGH,		"HIGH"	},
		{ BINARY + RELAT + OPR,		LE,			"LE"	},
		{ UNARY  + UOP3  + OPR,		LOW,		"LOW"	},
		{ BINARY + RELAT + OPR,		'<',		"LT"	},
		{ BINARY + MULT  + OPR,		MOD,		"MOD"	},
		{ BINARY + RELAT + OPR,		NE,			"NE"	},
		{ UNARY  + UOP2  + OPR,		NOT,		"NOT"	},
		{ BINARY + LOG2  + OPR,		OR,			"OR"	},
		{ BINARY + MULT  + OPR,		SHL,		"SHL"	},
		{ BINARY + MULT  + OPR,		SHR,		"SHR"	},
		{ REG,						'X',		"X"		},
		{ BINARY + LOG2  + OPR,		XOR,		"XOR"	},
		{ REG,						'Y',		"Y"		},
    };

    return bsearch(oprtbl,oprtbl + (sizeof(oprtbl) / sizeof(OPCODE)),nam);
}

static OPCODE *bsearch(OPCODE *lo, OPCODE *hi, char *nam) {
    SCRATCH int i;
    SCRATCH OPCODE *chk;

    for (;;) {
		chk = lo + (hi - lo) / 2;
		if (!(i = ustrcmp(chk -> oname,nam))) return chk;
		if (chk == lo) return NULL;
		if (i < 0) lo = chk;
		else hi = chk;
    }
}

static int ustrcmp(char *s, char *t) {
    SCRATCH int i;

    while (!(i = toupper(*s++) - toupper(*t)) && *t++);
    return i;
}

/*  Buffer storage for line listing routine.  This allows the listing	*/
/*  output routines to do all operations without the main routine	*/
/*  having to fool with it.						*/

static FILE *list = NULL;

/*  Listing file open routine.  If a listing file is already open, a	*/
/*  warning occurs.  If the listing file doesn't open correctly, a	*/
/*  fatal error occurs.  If no listing file is open, all calls to	*/
/*  lputs() and lclose() have no effect.				*/

void lopen(char *nam) {
    FILE *fopen();
    void fatal_error(), warning();

    if (list) warning(TWOLST);
    else if (!(list = fopen(nam,"w"))) fatal_error(LSTOPEN);
    return;
}

/*  Listing file line output routine.  This routine processes the	*/
/*  source line saved by popc() and the output of the line assembler in	*/
/*  buffer obj into a line of the listing.  If the disk fills up, a	*/
/*  fatal error occurs.							*/

void lputs() {
    SCRATCH int i, j;
    SCRATCH unsigned *o;
    void check_page(), fatal_error();

    if (list) {
		i = bytes;  o = obj;
		do {
			fprintf(list,"%c  ",errcode);
			if (listhex) {
				fprintf(list,"%04x  ",address);
				for (j = 4; j; --j) {
					if (i) { --i;  ++address;  fprintf(list," %02x",*o++); }
					else fprintf(list,"   ");
				}
			}
			else fprintf(list,"%18s","");
			fprintf(list,"   %s",line);  strcpy(line,"\n");
			check_page();
			if (ferror(list)) fatal_error(DSKFULL);
		} while (listhex && i);
    }
    return;
}

/*  Listing file close routine.  The symbol table is appended to the	*/
/*  listing in alphabetic order by symbol name, and the listing file is	*/
/*  closed.  If the disk fills up, a fatal error occurs.		*/

static int col = 0;

void lclose() {
    void fatal_error(), list_sym();

    if (list) {
		if (sroot) {
			list_sym(sroot);
			if (col) fprintf(list,"\n");
		}
		fprintf(list,"\f");
		if (ferror(list) || fclose(list) == EOF) fatal_error(DSKFULL);
    }
    return;
}

static void list_sym(SYMBOL *sp) {
    void check_page();

    if (sp) {
		list_sym(sp -> left);
		fprintf(list,"%04x  %-10s",sp -> valu,sp -> sname);
		if (col = ++col % SYMCOLS) fprintf(list,"    ");
		else {
			fprintf(list,"\n");
			if (sp -> right) check_page();
		}
		list_sym(sp -> right);
    }
    return;
}

static void check_page() {
    if (pagelen && !--listleft) eject = TRUE;
    if (eject) {
		eject = FALSE;  listleft = pagelen;  fprintf(list,"\f");
		if (title[0]) { listleft -= 2;  fprintf(list,"%s\n\n",title); }
    }
    return;
}

/*  Buffer storage for hex output file.  This allows the hex file	*/
/*  output routines to do all of the required buffering and record	*/
/*  forming without the	main routine having to fool with it.		*/

static FILE *hex = NULL;
static unsigned cnt = 0;
static unsigned addr = 0;
static unsigned sum = 0;
static unsigned buf[HEXSIZE];

/*  Hex file open routine.  If a hex file is already open, a warning	*/
/*  occurs.  If the hex file doesn't open correctly, a fatal error	*/
/*  occurs.  If no hex file is open, all calls to hputc(), hseek(), and	*/
/*  hclose() have no effect.						*/

void hopen(char *nam) {
    FILE *fopen();
    void fatal_error(), warning();

    if (hex) warning(TWOHEX);
    else if (!(hex = fopen(nam,"w"))) fatal_error(HEXOPEN);
    return;
}

/*  Hex file write routine.  The data byte is appended to the current	*/
/*  record.  If the record fills up, it gets written to disk.  If the	*/
/*  disk fills up, a fatal error occurs.				*/

void hputc(unsigned c) {
    void record();

    if (hex) {
		buf[cnt++] = c;
		if (cnt == HEXSIZE) record(0);
    }
    return;
}

/*  Hex file address set routine.  The specified address becomes the	*/
/*  load address of the next record.  If a record is currently open,	*/
/*  it gets written to disk.  If the disk fills up, a fatal error	*/
/*  occurs.								*/

void hseek(unsigned a) {
    void record();

    if (hex) {
		if (cnt) record(0);
		addr = a;
    }
    return;
}

/*  Hex file close routine.  Any open record is written to disk, the	*/
/*  EOF record is added, and file is closed.  If the disk fills up, a	*/
/*  fatal error occurs.							*/

void hclose() {
    void fatal_error(), record();

    if (hex) {
		if (cnt) record(0);
		record(1);
		if (fclose(hex) == EOF) fatal_error(DSKFULL);
    }
    return;
}

static void record(unsigned typ) {
    SCRATCH unsigned i;
    void fatal_error(), putb();

    putc(':',hex);  putb(cnt);  putb(high(addr));
    putb(low(addr));  putb(typ);
    for (i = 0; i < cnt; ++i) putb(buf[i]);
    putb(low(~sum + 1));  putc('\n',hex);

    addr += cnt;  cnt = 0;

    if (ferror(hex)) fatal_error(DSKFULL);
    return;
}

static void putb(unsigned b) {
    static char digit[] = "0123456789ABCDEF";

    putc(digit[b >> 4],hex);  putc(digit[b & 0x0f],hex);
    sum += b;  return;
}

/*  Error handler routine.  If the current error code is non-blank,	*/
/*  the error code is filled in and the	number of lines with errors	*/
/*  is adjusted.							*/

void error(char code) {
    if (errcode == ' ') { errcode = code;  ++errors; }
    return;
}

/*  Fatal error handler routine.  A message gets printed on the stderr	*/
/*  device, and the program bombs.					*/

void fatal_error(char *msg) {
    printf("Fatal Error -- %s\n",msg);
    exit(-1);
}

/*  Non-fatal error handler routine.  A message gets printed on the	*/
/*  stderr device, and the routine returns.				*/

void warning(char *msg) {
    printf("Warning -- %s\n",msg);
    return;
}

A  => test65.asm +223 -0
@@ 1,223 @@
;
;		      Test File for 6502 Cross-Assembler
;
; This file just contains all of the 6502 CPU's opcodes in numerical order.
;
ABS	EQU	$1234
IMM	EQU	$56
IND	EQU	$0078
ZP	EQU	$0090

;
; $00 - $0F
;
	BRK
	ORA	(IND, X)
	ORA	ZP
	ASL	ZP
	PHP
	ORA	#IMM
	ASL	A
	ORA	ABS
	ASL	ABS
;
; $10 - $1F
;
	BPL	*
	ORA	(IND), Y
	ORA	ZP, X
	ASL	ZP, X
	CLC
	ORA	ABS, Y
	ORA	ABS, X
	ASL	ABS, X
;
; $20 - $2F
;
	JSR	ABS
	AND	(IND, X)
	BIT	ZP
	AND	ZP
	ROL	ZP
	PLP
	AND	#IMM
	ROL	A
	BIT	ABS
	AND	ABS
	ROL	ABS
;
; $30 - $3F
;
	BMI	*
	AND	(IND), Y
	AND	ZP, X
	ROL	ZP, X
	SEC
	AND	ABS, Y
	AND	ABS, X
	ROL	ABS, X
;
; $40 - $4F
;
	RTI
	EOR	(IND, X)
	EOR	ZP
	LSR	ZP
	PHA
	EOR	#IMM
	LSR	A
	JMP	ABS
	EOR	ABS
	LSR	ABS
;
; $50 - $5F
;
	BVC	*
	EOR	(IND), Y
	EOR	ZP, X
	LSR	ZP, X
	CLI
	EOR	ABS, Y
	EOR	ABS, X
	LSR	ABS, X
;
; $60 - $6F
;
	RTS
	ADC	(IND, X)
	ADC	ZP
	ROR	ZP
	PLA
	ADC	#IMM
	ROR	A
	JMP	(ABS)
	ADC	ABS
	ROR	ABS
;
; $70 - $7F
;
	BVS	*
	ADC	(IND), Y
	ADC	ZP, X
	ROR	ZP, X
	SEI
	ADC	ABS, Y
	ADC	ABS, X
	ROR	ABS, X
;
; $80 - $8F
;
	STA	(IND, X)
	STY	ZP
	STA	ZP
	STX	ZP
	DEY
	TXA
	STY	ABS
	STA	ABS
	STX	ABS
;
; $90 - $9F
;
	BCC	*
	STA	(IND), Y
	STY	ZP, X
	STA	ZP, X
	STX	ZP, Y
	TYA
	STA	ABS, Y
	TXS
	STA	ABS, X
;
; $A0 - $AF
;
	LDY	#IMM
	LDA	(IND, X)
	LDX	#IMM
	LDY	ZP
	LDA	ZP
	LDX	ZP
	TAY
	LDA	#IMM
	TAX
	LDY	ABS
	LDA	ABS
	LDX	ABS
;
; $B0 - $BF
;
	BCS	*
	LDA	(IND), Y
	LDY	ZP, X
	LDA	ZP, X
	LDX	ZP, Y
	CLV
	LDA	ABS, Y
	TSX
	LDY	ABS, X
	LDA	ABS, X
	LDX	ABS, Y
;
; $C0 - $CF
;
	CPY	#IMM
	CMP	(IND, X)
	CPY	ZP
	CMP	ZP
	DEC	ZP
	INY
	CMP	#IMM
	DEX
	CPY	ABS
	CMP	ABS
	DEC	ABS
;
; $D0 - $DF
;
	BNE	*
	CMP	(IND), Y
	CMP	ZP, X
	DEC	ZP, X
	CLD
	CMP	ABS, Y
	CMP	ABS, X
	DEC	ABS, X
;
; $E0 - $EF
;
	CPX	#IMM
	SBC	(IND, X)
	CPX	ZP
	SBC	ZP
	INC	ZP
	INX
	SBC	#IMM
	NOP
	CPX	ABS
	SBC	ABS
	INC	ABS
;
; $F0 - $FF
;
	BEQ	*
	SBC	(IND), Y
	SBC	ZP, X
	INC	ZP, X
	SED
	SBC	ABS, Y
	SBC	ABS, X
	INC	ABS, X

	END

	SEI
	ADC	ABS, Y
	ADC	ABS, X
	ROR	ABS, X
;
; $80 - $8F
;
	STA	(IND, X)
	STY	ZP
	STA	ZP
	STX	ZP