mirror of
https://github.com/edmccard/twoapple-reboot.git
synced 2024-12-12 05:30:08 +00:00
Initial commit
This commit is contained in:
commit
44f39fe332
339
COPYING
Normal file
339
COPYING
Normal file
@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
51
README
Normal file
51
README
Normal file
@ -0,0 +1,51 @@
|
||||
=Twoapple Apple II Emulator=
|
||||
|
||||
http://twoapple.googlecode.com
|
||||
|
||||
==About==
|
||||
|
||||
Twoapple is an Apple II/IIe emulator written in the D programming language.
|
||||
|
||||
==Usage==
|
||||
|
||||
When started with no command-line options, Twoapple will run in II+ mode; use
|
||||
the "--iie" command-line option to run in IIe mode.
|
||||
|
||||
To select the ROM to use, choose a suitable file in the dialog which opens at
|
||||
program startup. (Currently, there are no checks for ROM file validity beyond a
|
||||
simple file size test).
|
||||
|
||||
The user interface is fairly simple at the moment: F12 acts as reset, and
|
||||
Pause/Break allows you to pause and restart the emulation (there is also a
|
||||
toolbar button for this). The "Boot" toolbar button acts as a power on reset,
|
||||
and the "Mono" toolbar button switches back and forth between color and
|
||||
monochrome display.
|
||||
|
||||
Opening, saving, and ejecting disk images can be done using the drive menus at
|
||||
the bottom of the window. The "WP" checkbox acts like a write-protect switch
|
||||
for the drive, rather than for individual images. (If a read-only image file is
|
||||
loaded, the write-protect status is set for the drive, and cannot be changed
|
||||
until another image is loaded.)
|
||||
|
||||
==Building from source==
|
||||
|
||||
To build twoapple, you will first need to install the Digital Mars D compiler
|
||||
(DMD) the D language bindings to GTK+ (gtkD), and the D language bindings to
|
||||
SDL (Derelict).
|
||||
|
||||
* DMD: http://www.digitalmars.com/d/1.0/changelog.html
|
||||
* gtkD: http://www.dsource.org/projects/dui
|
||||
* depends on GTK+2.0 (http://www.gtk.org) and GtkGLExt
|
||||
(http://gtkglext.sourceforge.net).
|
||||
* this patch (http://twoapple.googlecode.com/files/gtkD-pre3-ref-fix.patch)
|
||||
is also needed (from the gtkD directory,
|
||||
`patch -p1 < gtkD-pre3-ref-fix.patch`).
|
||||
* Derelict: http://www.dsource.org/projects/derelict
|
||||
* depends on SDL (http://www.sdl.org).
|
||||
* follow the build instructions to build the static libraries.
|
||||
* Twoapple: http://code.google.com/p/twoapple/downloads/list
|
||||
* (or via svn:
|
||||
`svn checkout http://twoapple.googlecode.com/svn/trunk twoapple`).
|
||||
* to build: `cd src; make DMDROOT=/path/to/dmd GTKD=/path/to/gtkD
|
||||
DERELICT=/path/to/derelict`
|
||||
|
23
src/Makefile
Normal file
23
src/Makefile
Normal file
@ -0,0 +1,23 @@
|
||||
DMD_OPTS = -c -version=CycleAccuracy -op -Jdata -I$(GTKD)/src -I$(GTKD)/srcgl \
|
||||
-I$(DERELICT)/DerelictSDL -I$(DERELICT)/DerelictUtil
|
||||
|
||||
GCC_OPTS = -m32 -lpthread -lm -lGL -ldl \
|
||||
-L$(DMDROOT)/dmd/lib -lphobos \
|
||||
-L$(GTKD) -lgtkd -lgtkdgl \
|
||||
-L$(DERELICT)/lib -lDerelictSDL -lDerelictUtil
|
||||
|
||||
ALL_SRC = $(shell find -name "*.d")
|
||||
ALL_OBJS = $(ALL_SRC:%.d=%.o)
|
||||
|
||||
all: ${ALL_OBJS}
|
||||
gcc ${ALL_OBJS} -o twoapple ${GCC_OPTS}
|
||||
|
||||
clean:
|
||||
rm -rf twoapple ${ALL_OBJS}
|
||||
|
||||
twoapple.o: twoapple.d
|
||||
dmd $(DFLAGS) -inline -O $(DMD_OPTS) $<
|
||||
|
||||
%.o: %.d
|
||||
dmd $(DFLAGS) -inline -release -O $(DMD_OPTS) $<
|
||||
|
140
src/d6502/base.d
Normal file
140
src/d6502/base.d
Normal file
@ -0,0 +1,140 @@
|
||||
/+
|
||||
+ d6502/base.d
|
||||
+
|
||||
+ Copyright: 2007 Gerald Stocker
|
||||
+
|
||||
+ This file is part of Twoapple.
|
||||
+
|
||||
+ Twoapple is free software; you can redistribute it and/or modify
|
||||
+ it under the terms of the GNU General Public License as published by
|
||||
+ the Free Software Foundation; either version 2 of the License, or
|
||||
+ (at your option) any later version.
|
||||
+
|
||||
+ Twoapple is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ GNU General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU General Public License
|
||||
+ along with Twoapple; if not, write to the Free Software
|
||||
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
+/
|
||||
|
||||
module d6502.base;
|
||||
|
||||
string hexByte(int decByte)
|
||||
{
|
||||
int highNybble = (decByte & 0xF0) >> 4;
|
||||
int lowNybble = decByte & 0x0F;
|
||||
|
||||
string digits = "0123456789ABCDEF";
|
||||
|
||||
return digits[highNybble..(highNybble + 1)] ~
|
||||
digits[lowNybble..(lowNybble + 1)];
|
||||
}
|
||||
|
||||
final class StatusRegister
|
||||
{
|
||||
bool carry, decimal, interrupt, overflow;
|
||||
ubyte zero_, negative_;
|
||||
|
||||
ubyte toByte()
|
||||
{
|
||||
return (carry ? 0x01 : 0) |
|
||||
((zero_ == 0) ? 0x02 : 0) |
|
||||
(interrupt ? 0x04 : 0) |
|
||||
(decimal ? 0x08 : 0) |
|
||||
0x30 | // break and reserved both set
|
||||
(overflow ? 0x40 : 0) |
|
||||
(negative_ & 0x80);
|
||||
}
|
||||
|
||||
void fromByte(ubyte val)
|
||||
{
|
||||
carry = ((val & 0x01) != 0);
|
||||
zero_ = ((val & 0x02) ? 0 : 1);
|
||||
interrupt = ((val & 0x04) != 0);
|
||||
decimal = ((val & 0x08) != 0);
|
||||
overflow = ((val & 0x40) != 0);
|
||||
negative_ = val;
|
||||
}
|
||||
}
|
||||
|
||||
class CpuBase
|
||||
{
|
||||
static string AbstractOpcodes()
|
||||
{
|
||||
string abstractOpcodes;
|
||||
for (int op = 0; op < 256; ++op)
|
||||
abstractOpcodes ~= "abstract void opcode" ~ hexByte(op) ~ "();\n";
|
||||
return abstractOpcodes;
|
||||
}
|
||||
|
||||
mixin(AbstractOpcodes());
|
||||
|
||||
ushort programCounter;
|
||||
ubyte accumulator, xIndex, yIndex, stackPointer;
|
||||
final StatusRegister flag;
|
||||
|
||||
bool signalActive, irqActive, resetActive, nmiActive, nmiArmed;
|
||||
|
||||
ushort opcodePC;
|
||||
ubyte opcode, operand1, operand2;
|
||||
|
||||
final ubyte[] save()
|
||||
{
|
||||
ubyte[] data = new ubyte[12];
|
||||
data[0] = programCounter & 0xFF;
|
||||
data[1] = programCounter >> 8;
|
||||
data[2] = accumulator;
|
||||
data[3] = xIndex;
|
||||
data[4] = yIndex;
|
||||
data[5] = stackPointer;
|
||||
data[6] = flag.toByte();
|
||||
data[7] = (signalActive ? 1 : 0);
|
||||
data[8] = (irqActive ? 1 : 0);
|
||||
data[9] = (resetActive ? 1 : 0);
|
||||
data[10] = (nmiActive ? 1 : 0);
|
||||
data[11] = (nmiArmed ? 1 : 0);
|
||||
return data;
|
||||
}
|
||||
|
||||
final void restore(ubyte[] data)
|
||||
{
|
||||
assert (data.length >= 12);
|
||||
|
||||
programCounter = data[0] | (data[1] << 8);
|
||||
accumulator = data[2];
|
||||
xIndex = data[3];
|
||||
yIndex = data[4];
|
||||
stackPointer = data[5];
|
||||
flag.fromByte(data[6]);
|
||||
signalActive = ((data[7] == 0) ? false : true);
|
||||
irqActive = ((data[8] == 0) ? false : true);
|
||||
resetActive = ((data[9] == 0) ? false : true);
|
||||
nmiActive = ((data[10] == 0) ? false : true);
|
||||
nmiArmed = ((data[11] == 0) ? false : true);
|
||||
}
|
||||
|
||||
final void reboot()
|
||||
{
|
||||
restore([0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 1]);
|
||||
}
|
||||
|
||||
ubyte delegate(ushort addr) memoryRead;
|
||||
void delegate(ushort addr, ubyte val) memoryWrite;
|
||||
debug(disassemble)
|
||||
{
|
||||
string delegate(ushort addr) memoryName;
|
||||
}
|
||||
version(CycleAccuracy) void delegate() tick;
|
||||
version(CumulativeCycles) void delegate(int cycles) ticks;
|
||||
|
||||
abstract void run(bool continuous);
|
||||
abstract void stop();
|
||||
version(CycleAccuracy) abstract bool checkFinalCycle();
|
||||
abstract void resetLow();
|
||||
abstract void nmiLow(bool signalLow);
|
||||
abstract void irqLow(bool signalLow);
|
||||
}
|
||||
|
238
src/d6502/cmos.d
Normal file
238
src/d6502/cmos.d
Normal file
@ -0,0 +1,238 @@
|
||||
/+
|
||||
+ d6502/cmos.d
|
||||
+
|
||||
+ Copyright: 2007 Gerald Stocker
|
||||
+
|
||||
+ This file is part of Twoapple.
|
||||
+
|
||||
+ Twoapple is free software; you can redistribute it and/or modify
|
||||
+ it under the terms of the GNU General Public License as published by
|
||||
+ the Free Software Foundation; either version 2 of the License, or
|
||||
+ (at your option) any later version.
|
||||
+
|
||||
+ Twoapple is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ GNU General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU General Public License
|
||||
+ along with Twoapple; if not, write to the Free Software
|
||||
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
+/
|
||||
|
||||
import d6502.base;
|
||||
import d6502.cpu;
|
||||
|
||||
class Cmos : Cpu
|
||||
{
|
||||
this()
|
||||
{
|
||||
super();
|
||||
spuriousAddress = &programCounter;
|
||||
}
|
||||
|
||||
final override void do_IRQ_or_NMI(ushort vector)
|
||||
{
|
||||
super.do_IRQ_or_NMI(vector);
|
||||
flag.decimal = false;
|
||||
}
|
||||
|
||||
final override void doReset()
|
||||
{
|
||||
super.doReset();
|
||||
flag.decimal = false;
|
||||
}
|
||||
|
||||
final override void dec_addWithCarry(ubyte val)
|
||||
{
|
||||
super.dec_addWithCarry(val);
|
||||
peek(programCounter);
|
||||
flag.zero_ = flag.negative_ = accumulator;
|
||||
}
|
||||
|
||||
final override void dec_subWithCarry(ubyte val)
|
||||
{
|
||||
super.dec_subWithCarry(val);
|
||||
peek(programCounter);
|
||||
flag.zero_ = flag.negative_ = accumulator;
|
||||
}
|
||||
|
||||
final void addrZeropageI()
|
||||
{
|
||||
ubyte vector = readByteOperand();
|
||||
primaryAddress = readWord(vector, cast(ubyte)(vector + 1));
|
||||
}
|
||||
|
||||
final void addrNone()
|
||||
{
|
||||
version(CumulativeCycles) ticks(totalCycles);
|
||||
}
|
||||
|
||||
final ubyte testSet(ubyte val)
|
||||
{
|
||||
flag.zero_ = val & accumulator;
|
||||
return val | accumulator;
|
||||
}
|
||||
|
||||
final ubyte testReset(ubyte val)
|
||||
{
|
||||
flag.zero_ = val & accumulator;
|
||||
return val & ~accumulator;
|
||||
}
|
||||
|
||||
static string RMW(string action)
|
||||
{
|
||||
return "peek(primaryAddress);\n" ~
|
||||
"writeFinal(primaryAddress, (flag.zero_ = flag.negative_ = " ~
|
||||
action ~ "(readVal = read(primaryAddress))));\n";
|
||||
}
|
||||
|
||||
static string TestModify(string action)
|
||||
{
|
||||
return "peek(primaryAddress);\n" ~
|
||||
"writeFinal(primaryAddress, " ~
|
||||
action ~ "(readVal = read(primaryAddress)));\n";
|
||||
}
|
||||
|
||||
static string ReadNOP()
|
||||
{
|
||||
return "readVal = readFinal(primaryAddress);\n";
|
||||
}
|
||||
|
||||
static string ManualAddress(string name, int[] opcodes,
|
||||
string mode)
|
||||
{
|
||||
string modes = "[[\"" ~ name ~ "\", \"NA\"], \n";
|
||||
for (int op = 0; op < opcodes.length; ++op)
|
||||
{
|
||||
int opcode = opcodes[op];
|
||||
modes ~= "[\"" ~ hexByte(opcode) ~ "\", \"" ~ mode ~ "\"]";
|
||||
if (op != (opcodes.length - 1)) modes ~= ", ";
|
||||
modes ~= "\n";
|
||||
}
|
||||
return modes ~ "]\n";
|
||||
}
|
||||
|
||||
mixin(Opcode(mixin(Type2Address(
|
||||
"ASL", "Read", [0x06, 0x0E, 0x16, 0x1E])),
|
||||
RMW("shiftLeft")));
|
||||
mixin(Opcode(mixin(Type2Address(
|
||||
"LSR", "Read", [0x46, 0x4E, 0x56, 0x5E])),
|
||||
RMW("shiftRight")));
|
||||
mixin(Opcode(mixin(Type2Address(
|
||||
"ROL", "Read", [0x26, 0x2E, 0x36, 0x3E])),
|
||||
RMW("rotateLeft")));
|
||||
mixin(Opcode(mixin(Type2Address(
|
||||
"ROR", "Read", [0x66, 0x6E, 0x76, 0x7E])),
|
||||
RMW("rotateRight")));
|
||||
mixin(Opcode(mixin(Type2Address(
|
||||
"INC", "Read", [0xE6, 0xEE, 0xF6, 0xFE])),
|
||||
RMW("increment")));
|
||||
mixin(Opcode(mixin(Type2Address(
|
||||
"DEC", "Read", [0xC6, 0xCE, 0xD6, 0xDE])),
|
||||
RMW("decrement")));
|
||||
|
||||
mixin(Opcode(mixin(Type2Address(
|
||||
"BIT", "Read", [0x34, 0x3C])),
|
||||
BitTest()));
|
||||
mixin(Opcode([["ORA", "Read"], ["12", "ZeropageI()"]],
|
||||
Read("accumulator |=")));
|
||||
mixin(Opcode([["AND", "Read"], ["32", "ZeropageI()"]],
|
||||
Read("accumulator &=")));
|
||||
mixin(Opcode([["EOR", "Read"], ["52", "ZeropageI()"]],
|
||||
Read("accumulator ^=")));
|
||||
mixin(Opcode([["LDA", "Read"], ["B2", "ZeropageI()"]],
|
||||
Read("accumulator =")));
|
||||
mixin(Opcode([["CMP", "Read"], ["D2", "ZeropageI()"]],
|
||||
Compare("accumulator")));
|
||||
mixin(Opcode([["ADC", "Read"], ["72", "ZeropageI()"]],
|
||||
Decimal("addWithCarry")));
|
||||
mixin(Opcode([["SBC", "Read"], ["F2", "ZeropageI()"]],
|
||||
Decimal("subWithCarry")));
|
||||
mixin(Opcode([["STA", "Write"], ["92", "ZeropageI()"]],
|
||||
Write("accumulator")));
|
||||
|
||||
mixin(RegisterOpcode("DEA", "3A", "accumulator -= 1"));
|
||||
mixin(RegisterOpcode("INA", "1A", "accumulator += 1"));
|
||||
mixin(SimpleOpcode("PHX", "DA", "push(xIndex)"));
|
||||
mixin(SimpleOpcode("PHY", "5A", "push(yIndex)"));
|
||||
mixin(RegisterOpcode("PLX", "FA", "xIndex = pull()"));
|
||||
mixin(RegisterOpcode("PLY", "7A", "yIndex = pull()"));
|
||||
mixin(BranchOpcode("BRA", "80", "true"));
|
||||
|
||||
mixin(Opcode([["TRB", "Read"],
|
||||
["14", "Zeropage()"], ["1C", "Absolute"]],
|
||||
TestModify("testReset")));
|
||||
mixin(Opcode(mixin(Type2Address(
|
||||
"TSB", "Read", [0x04, 0x0C])),
|
||||
TestModify("testSet")));
|
||||
mixin(Opcode([["STZ", "Write"],
|
||||
["64", "Zeropage()"], ["74", "ZeropageX()"],
|
||||
["9C", "Absolute()"], ["9E", "AbsoluteX(true)"]],
|
||||
Write("0")));
|
||||
|
||||
mixin(Opcode(mixin(ManualAddress(
|
||||
"NOP", [0x02, 0x22, 0x42, 0x62, 0x82, 0xC2, 0xE2],
|
||||
"Immediate")),
|
||||
ReadNOP()));
|
||||
mixin(Opcode(mixin(ManualAddress(
|
||||
"NOP", [0x44],
|
||||
"Zeropage()")),
|
||||
ReadNOP()));
|
||||
mixin(Opcode(mixin(ManualAddress(
|
||||
"NOP", [0x54, 0xD4, 0xF4],
|
||||
"ZeropageX()")),
|
||||
ReadNOP()));
|
||||
mixin(Opcode(mixin(ManualAddress(
|
||||
"NOP", [0xDC, 0xFC],
|
||||
"AbsoluteX(false)")),
|
||||
ReadNOP()));
|
||||
mixin(Opcode(mixin(ManualAddress(
|
||||
"NOP", [0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73, 0x83, 0x93,
|
||||
0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3, 0x07, 0x17, 0x27, 0x37,
|
||||
0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7,
|
||||
0xE7, 0xF7, 0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B,
|
||||
0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB, 0x0F, 0x1F,
|
||||
0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F, 0x8F, 0x9F, 0xAF, 0xBF,
|
||||
0xCF, 0xDF, 0xEF, 0xFF], "None()")),
|
||||
""));
|
||||
|
||||
/* NOP8 */
|
||||
void opcode5C()
|
||||
{
|
||||
readByteOperand();
|
||||
peek(programCounter);
|
||||
peek(0xFF00 | operand1);
|
||||
peek(0xFFFF);
|
||||
peek(0xFFFF);
|
||||
peek(0xFFFF);
|
||||
peek(0xFFFF);
|
||||
}
|
||||
|
||||
/* JMP ($$$$) */
|
||||
override void opcode6C()
|
||||
{
|
||||
ushort vector = readWordOperand();
|
||||
peek(programCounter);
|
||||
programCounter = readWord(vector, vector + 1);
|
||||
version(CumulativeCycles) ticks(totalCycles);
|
||||
}
|
||||
|
||||
/* JMP ($$$$,X) */
|
||||
void opcode7C()
|
||||
{
|
||||
baseAddress = readWordOperand();
|
||||
peek(programCounter);
|
||||
ushort vector = baseAddress + xIndex;
|
||||
programCounter = readWord(vector, vector + 1);
|
||||
version(CumulativeCycles) ticks(totalCycles);
|
||||
}
|
||||
|
||||
/* BIT #$$ */
|
||||
void opcode89()
|
||||
{
|
||||
readVal = operand1 = readFinal(programCounter++);
|
||||
flag.zero_ = accumulator & readVal;
|
||||
}
|
||||
}
|
||||
|
758
src/d6502/cpu.d
Normal file
758
src/d6502/cpu.d
Normal file
@ -0,0 +1,758 @@
|
||||
/+
|
||||
+ d6502/cpu.d
|
||||
+
|
||||
+ Copyright: 2007 Gerald Stocker
|
||||
+
|
||||
+ This file is part of Twoapple.
|
||||
+
|
||||
+ Twoapple is free software; you can redistribute it and/or modify
|
||||
+ it under the terms of the GNU General Public License as published by
|
||||
+ the Free Software Foundation; either version 2 of the License, or
|
||||
+ (at your option) any later version.
|
||||
+
|
||||
+ Twoapple is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
+ GNU General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU General Public License
|
||||
+ along with Twoapple; if not, write to the Free Software
|
||||
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
+/
|
||||
|
||||
import d6502.base;
|
||||
|
||||
class Cpu : CpuBase
|
||||
{
|
||||
static string InitOpcodes()
|
||||
{
|
||||
string initCalls;
|
||||
for (int op = 0; op < 256; ++op)
|
||||
{
|
||||
initCalls ~= "opcodes[0x" ~ hexByte(op) ~ "] = &opcode" ~
|
||||
hexByte(op) ~ ";\n";
|
||||
}
|
||||
return initCalls;
|
||||
}
|
||||
|
||||
this()
|
||||
{
|
||||
mixin(InitOpcodes());
|
||||
flag = new StatusRegister();
|
||||
}
|
||||
|
||||
const ushort STACK_BASE = 0x0100;
|
||||
const ushort NMI_VECTOR = 0xFFFA;
|
||||
const ushort RESET_VECTOR = 0xFFFC;
|
||||
const ushort IRQ_VECTOR = 0xFFFE;
|
||||
|
||||
void delegate()[256] opcodes;
|
||||
bool continueExecution;
|
||||
version(CycleAccuracy) bool finalCycle;
|
||||
version(CumulativeCycles) int totalCycles;
|
||||
|
||||
debug(disassemble)
|
||||
{
|
||||
import hacking.debugger;
|
||||
import std.stdio;
|
||||
}
|
||||
final override void run(bool continuous)
|
||||
{
|
||||
assert ((memoryRead !is null) && (memoryWrite !is null));
|
||||
version(CycleAccuracy) assert (tick !is null);
|
||||
|
||||
continueExecution = continuous;
|
||||
do
|
||||
{
|
||||
if (signalActive) handleSignals();
|
||||
|
||||
opcodePC = programCounter;
|
||||
opcode = read(programCounter++);
|
||||
version(CycleAccuracy) finalCycle = false;
|
||||
version(CumulativeCycles) totalCycles = 0;
|
||||
|
||||
/+ TODO: call sync delegate +/
|
||||
|
||||
opcodes[opcode]();
|
||||
debug(disassemble)
|
||||
{
|
||||
writefln(Debugger.disassemble(this, cmosMap)
|
||||
~ Debugger.displayRegisters(this));
|
||||
}
|
||||
} while (continueExecution);
|
||||
}
|
||||
|
||||
final override void stop()
|
||||
{
|
||||
continueExecution = false;
|
||||
}
|
||||
|
||||
version(CycleAccuracy)
|
||||
{
|
||||
final override bool checkFinalCycle()
|
||||
{
|
||||
return finalCycle;
|
||||
}
|
||||
}
|
||||
|
||||
final override void resetLow()
|
||||
{
|
||||
resetActive = signalActive = true;
|
||||
}
|
||||
|
||||
final override void nmiLow(bool signalLow)
|
||||
{
|
||||
nmiActive = signalLow;
|
||||
if (!signalLow) nmiArmed = true;
|
||||
signalActive = testSignals();
|
||||
}
|
||||
|
||||
final override void irqLow(bool signalLow)
|
||||
{
|
||||
irqActive = signalLow;
|
||||
signalActive = testSignals();
|
||||
}
|
||||
|
||||
final bool testSignals()
|
||||
{
|
||||
return (resetActive || nmiActive || irqActive);
|
||||
}
|
||||
|
||||
final void handleSignals()
|
||||
{
|
||||
bool checkNMI()
|
||||
{
|
||||
if (nmiActive && nmiArmed)
|
||||
{
|
||||
nmiArmed = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (resetActive) doReset();
|
||||
else if (checkNMI()) do_IRQ_or_NMI(NMI_VECTOR);
|
||||
else if ((!flag.interrupt) && irqActive) do_IRQ_or_NMI(IRQ_VECTOR);
|
||||
}
|
||||
|
||||
void do_IRQ_or_NMI(ushort vector)
|
||||
{
|
||||
doInterrupt(vector, (flag.toByte() & ~0x10));
|
||||
}
|
||||
|
||||
final void doInterrupt(ushort vector, ubyte statusByte)
|
||||
{
|
||||
pushWord(programCounter);
|
||||
push(statusByte);
|
||||
flag.interrupt = true;
|
||||
programCounter = readWord(vector, vector + 1);
|
||||
version(CumulativeCycles) ticks(totalCycles);
|
||||
}
|
||||
|
||||
void doReset()
|
||||
{
|
||||
version(CycleAccuracy)
|
||||
{
|
||||
tick(); tick();
|
||||
}
|
||||
version(CumulativeCycles)
|
||||
{
|
||||
totalCycles += 2;
|
||||
}
|
||||
|
||||
peek(STACK_BASE + stackPointer);
|
||||
--stackPointer;
|
||||
peek(STACK_BASE + stackPointer);
|
||||
--stackPointer;
|
||||
peek(STACK_BASE + stackPointer);
|
||||
--stackPointer;
|
||||
|
||||
flag.interrupt = true;
|
||||
resetActive = false;
|
||||
signalActive = testSignals();
|
||||
|
||||
programCounter = readWord(RESET_VECTOR, RESET_VECTOR + 1);
|
||||
version(CumulativeCycles) ticks(totalCycles);
|
||||
}
|
||||
|
||||
final ubyte read(ushort addr)
|
||||
{
|
||||
version(CycleAccuracy) tick();
|
||||
version(CumulativeCycles) ++totalCycles;
|
||||
return memoryRead(addr);
|
||||
}
|
||||
|
||||
final void write(ushort addr, ubyte val)
|
||||
{
|
||||
version(CycleAccuracy) tick();
|
||||
version(CumulativeCycles) ++totalCycles;
|
||||
memoryWrite(addr, val);
|
||||
}
|
||||
|
||||
final void peek(ushort addr)
|
||||
{
|
||||
version(CycleAccuracy) tick();
|
||||
version(CumulativeCycles) ++totalCycles;
|
||||
version(StrictMemoryAccess) memoryRead(addr);
|
||||
}
|
||||
|
||||
final void poke(ushort addr, ubyte val)
|
||||
{
|
||||
version(CycleAccuracy) tick();
|
||||
version(CumulativeCycles) ++totalCycles;
|
||||
version(StrictMemoryAccess) memoryWrite(addr, val);
|
||||
}
|
||||
|
||||
final ubyte readFinal(ushort addr)
|
||||
{
|
||||
version(CycleAccuracy)
|
||||
{
|
||||
finalCycle = true;
|
||||
tick();
|
||||
}
|
||||
version(CumulativeCycles) ticks(++totalCycles);
|
||||
return memoryRead(addr);
|
||||
}
|
||||
|
||||
final void writeFinal(ushort addr, ubyte val)
|
||||
{
|
||||
version(CycleAccuracy)
|
||||
{
|
||||
finalCycle = true;
|
||||
tick();
|
||||
}
|
||||
version(CumulativeCycles) ticks(++totalCycles);
|
||||
memoryWrite(addr, val);
|
||||
}
|
||||
|
||||
final ushort readWord(ushort addrLo, ushort addrHi)
|
||||
{
|
||||
ushort word = read(addrLo);
|
||||
return word | (read(addrHi) << 8);
|
||||
}
|
||||
|
||||
final void push(ubyte val)
|
||||
{
|
||||
write((STACK_BASE + stackPointer), val);
|
||||
--stackPointer;
|
||||
/+ TODO: call stack overflow delegate +/
|
||||
}
|
||||
|
||||
final void pushWord(ushort val)
|
||||
{
|
||||
push(val >> 8);
|
||||
push(val & 0xFF);
|
||||
}
|
||||
|
||||
final ubyte readStack()
|
||||
{
|
||||
++stackPointer;
|
||||
/+ TODO: call stack underflow delegate +/
|
||||
return read(STACK_BASE + stackPointer);
|
||||
}
|
||||
|
||||
final ubyte pull()
|
||||
{
|
||||
peek(STACK_BASE + stackPointer);
|
||||
return readStack();
|
||||
}
|
||||
|
||||
final ushort pullWord()
|
||||
{
|
||||
ushort word = pull();
|
||||
return word | (readStack() << 8);
|
||||
}
|
||||
|
||||
final ubyte readByteOperand()
|
||||
{
|
||||
return (operand1 = read(programCounter++));
|
||||
}
|
||||
|
||||
final ushort readWordOperand()
|
||||
{
|
||||
operand1 = read(programCounter++);
|
||||
operand2 = read(programCounter++);
|
||||
return (operand1 | (operand2 << 8));
|
||||
}
|
||||
|
||||
ushort* spuriousAddress;
|
||||
ushort badAddress, baseAddress, primaryAddress;
|
||||
ubyte readVal, writeVal;
|
||||
|
||||
final ushort tryShortcut(bool noShortcut, ushort goodAddress)
|
||||
{
|
||||
badAddress = (baseAddress & 0xFF00) | cast(ubyte)goodAddress;
|
||||
if (noShortcut || (badAddress != goodAddress)) peek(*spuriousAddress);
|
||||
return goodAddress;
|
||||
}
|
||||
|
||||
final void addrRelative(byte offset)
|
||||
{
|
||||
peek(programCounter);
|
||||
baseAddress = programCounter;
|
||||
programCounter = tryShortcut(false, programCounter + offset);
|
||||
}
|
||||
|
||||
final void addrZeropage()
|
||||
{
|
||||
primaryAddress = readByteOperand();
|
||||
}
|
||||
|
||||
final void addrAbsolute()
|
||||
{
|
||||
primaryAddress = readWordOperand();
|
||||
}
|
||||
|
||||
final void addrZeropageX()
|
||||
{
|
||||
baseAddress = badAddress = readByteOperand();
|
||||
peek(*spuriousAddress);
|
||||
primaryAddress = cast(ubyte)(baseAddress + xIndex);
|
||||
}
|
||||
|
||||
final void addrZeropageY()
|
||||
{
|
||||
baseAddress = badAddress = readByteOperand();
|
||||
peek(*spuriousAddress);
|
||||
primaryAddress = cast(ubyte)(baseAddress + yIndex);
|
||||
}
|
||||
|
||||
final void addrIndirectX()
|
||||
{
|
||||
baseAddress = badAddress = readByteOperand();
|
||||
peek(*spuriousAddress);
|
||||
ushort vector = cast(ubyte)(baseAddress + xIndex);
|
||||
primaryAddress = readWord(vector, cast(ubyte)(vector + 1));
|
||||
}
|
||||
|
||||
final void addrAbsoluteX(bool write)
|
||||
{
|
||||
baseAddress = readWordOperand();
|
||||
primaryAddress = tryShortcut(write, baseAddress + xIndex);
|
||||
}
|
||||
|
||||
final void addrAbsoluteY(bool write)
|
||||
{
|
||||
baseAddress = readWordOperand();
|
||||
primaryAddress = tryShortcut(write, baseAddress + yIndex);
|
||||
}
|
||||
|
||||
final void addrIndirectY(bool write)
|
||||
{
|
||||
ubyte vector = readByteOperand();
|
||||
baseAddress = readWord(vector, cast(ubyte)(vector + 1));
|
||||
primaryAddress = tryShortcut(write, baseAddress + yIndex);
|
||||
}
|
||||
|
||||
void dec_addWithCarry(ubyte val)
|
||||
{
|
||||
uint bcdSum = (accumulator & 0x0F) + (val & 0x0F) + flag.carry;
|
||||
|
||||
if (bcdSum >= 10)
|
||||
bcdSum = (bcdSum - 10) | 0x10;
|
||||
bcdSum += (accumulator & 0xF0) + (val & 0xF0);
|
||||
|
||||
flag.negative_ = bcdSum;
|
||||
flag.overflow =
|
||||
(!((accumulator ^ val) & 0x80)) && ((val ^ bcdSum) & 0x80);
|
||||
|
||||
if (bcdSum > 0x9f)
|
||||
bcdSum += 0x60;
|
||||
|
||||
flag.zero_ = accumulator + val + (flag.carry ? 1 : 0);
|
||||
flag.carry = (bcdSum > 0xFF);
|
||||
|
||||
accumulator = bcdSum;
|
||||
}
|
||||
|
||||
void dec_subWithCarry(ubyte val)
|
||||
{
|
||||
uint diff = accumulator - val - (flag.carry ? 0 : 1);
|
||||
|
||||
flag.overflow =
|
||||
((accumulator ^ diff) & 0x80) &&
|
||||
((accumulator ^ val) & 0x80);
|
||||
|
||||
uint al = (accumulator & 0x0F) - (val & 0x0F) -
|
||||
(flag.carry ? 0 : 1);
|
||||
uint ah = (accumulator >> 4) - (val >> 4);
|
||||
if (al & 0x10)
|
||||
{
|
||||
al -= 6;
|
||||
ah--;
|
||||
}
|
||||
if (ah & 0x10)
|
||||
ah -= 6;
|
||||
|
||||
flag.carry = (diff < 0x100);
|
||||
flag.zero_ = flag.negative_ = diff;
|
||||
|
||||
accumulator = (ah << 4) + (al & 0x0F);
|
||||
}
|
||||
|
||||
final void hex_addWithCarry(ubyte val)
|
||||
{
|
||||
uint sum = accumulator + val + flag.carry;
|
||||
|
||||
flag.overflow =
|
||||
(!((accumulator ^ val) & 0x80)) && ((val ^ sum) & 0x80);
|
||||
flag.carry = (sum > 0xFF);
|
||||
|
||||
flag.zero_ = flag.negative_ = (accumulator = sum);
|
||||
}
|
||||
|
||||
final void hex_subWithCarry(ubyte val)
|
||||
{
|
||||
uint diff = accumulator - val - (flag.carry ? 0 : 1);
|
||||
|
||||
flag.overflow =
|
||||
((accumulator ^ diff) & 0x80) &&
|
||||
((accumulator ^ val) & 0x80);
|
||||
flag.carry = (diff < 0x100);
|
||||
|
||||
flag.zero_ = flag.negative_ = (accumulator = diff);
|
||||
}
|
||||
|
||||
final ubyte compare(ubyte reg, ubyte val)
|
||||
{
|
||||
flag.carry = (reg >= val);
|
||||
return reg - val;
|
||||
}
|
||||
|
||||
final void bitTest(ubyte val)
|
||||
{
|
||||
flag.negative_ = val;
|
||||
flag.zero_ = accumulator & val;
|
||||
flag.overflow = ((val & 0x40) != 0);
|
||||
}
|
||||
|
||||
final ubyte shiftLeft(ubyte val)
|
||||
{
|
||||
flag.carry = (val > 0x7F);
|
||||
return val << 1;
|
||||
}
|
||||
|
||||
final ubyte rotateLeft(ubyte val)
|
||||
{
|
||||
bool oldCarry = flag.carry;
|
||||
flag.carry = (val > 0x7F);
|
||||
val = (val << 1 | (oldCarry ? 1 : 0));
|
||||
return val;
|
||||
}
|
||||
|
||||
final ubyte shiftRight(ubyte val)
|
||||
{
|
||||
flag.carry = ((val & 0x01) != 0);
|
||||
return val >> 1;
|
||||
}
|
||||
|
||||
final ubyte rotateRight(ubyte val)
|
||||
{
|
||||
bool oldCarry = flag.carry;
|
||||
flag.carry = ((val & 0x01) != 0);
|
||||
val = (val >> 1 | (oldCarry ? 0x80 : 0));
|
||||
return val;
|
||||
}
|
||||
|
||||
final ubyte increment(ubyte val)
|
||||
{
|
||||
return val + 1;
|
||||
}
|
||||
|
||||
final ubyte decrement(ubyte val)
|
||||
{
|
||||
return val - 1;
|
||||
}
|
||||
|
||||
static string SimpleOpcode(string name, string opcode, string action)
|
||||
{
|
||||
string code = "peek(programCounter);\n";
|
||||
version(CumulativeCycles) code ~= "ticks(totalCycles);\n";
|
||||
code ~= (action == "") ? "" : (action ~ ";");
|
||||
return "override void opcode" ~ opcode ~ "()\n{\n" ~ code ~ "\n}\n";
|
||||
}
|
||||
|
||||
static string UpdateNZ(string action)
|
||||
{
|
||||
return "flag.zero_ = flag.negative_ = (" ~ action ~ ");" ~ "\n";
|
||||
}
|
||||
|
||||
static string RegisterOpcode(string name, string opcode, string action)
|
||||
{
|
||||
string code = "peek(programCounter);\n";
|
||||
version(CumulativeCycles) code ~= "ticks(totalCycles);\n";
|
||||
return "override void opcode" ~ opcode ~ "()\n{\n" ~
|
||||
code ~ UpdateNZ(action) ~ "}\n";
|
||||
}
|
||||
|
||||
static string BranchOpcode(string name, string opcode, string action)
|
||||
{
|
||||
string code = "readByteOperand();\n" ~
|
||||
"if (" ~ action ~ ") addrRelative(cast(byte)operand1);\n";
|
||||
version(CumulativeCycles) code ~= "ticks(totalCycles);\n";
|
||||
return "override void opcode" ~ opcode ~ "()\n{\n" ~ code ~ "}\n";
|
||||
}
|
||||
|
||||
static string Type1Address(string name, string rw, int[] opcodes)
|
||||
{
|
||||
string type = (rw == "Write") ? "true" : "false";
|
||||
string modes = "[[\"" ~ name ~ "\", \"" ~ rw ~ "\"], \n";
|
||||
for (int op = 0; op < opcodes.length; ++op)
|
||||
{
|
||||
int opcode = opcodes[op];
|
||||
modes ~= "[\"" ~ hexByte(opcode) ~ "\", \"";
|
||||
switch ((opcode & 0b00011100) >> 2)
|
||||
{
|
||||
case 0:
|
||||
modes ~= "IndirectX()";
|
||||
break;
|
||||
case 1:
|
||||
modes ~= "Zeropage()";
|
||||
break;
|
||||
case 2:
|
||||
modes ~= "Immediate";
|
||||
break;
|
||||
case 3:
|
||||
modes ~= "Absolute()";
|
||||
break;
|
||||
case 4:
|
||||
modes ~= "IndirectY("~ type ~ ")";
|
||||
break;
|
||||
case 5:
|
||||
modes ~= "ZeropageX()";
|
||||
break;
|
||||
case 6:
|
||||
modes ~= "AbsoluteY(" ~ type ~ ")";
|
||||
break;
|
||||
case 7:
|
||||
modes ~= "AbsoluteX(" ~ type ~ ")";
|
||||
break;
|
||||
}
|
||||
modes ~= "\"]";
|
||||
if (op != (opcodes.length - 1)) modes ~= ", ";
|
||||
modes ~= "\n";
|
||||
}
|
||||
return modes ~ "]\n";
|
||||
}
|
||||
|
||||
static string Type2Address(string name, string rw, int[] opcodes)
|
||||
{
|
||||
string type = (rw == "Write") ? "true" : "false";
|
||||
string index = (name[2] == 'X') ? "Y" : "X";
|
||||
string modes = "[[\"" ~ name ~ "\", \"" ~ rw ~ "\"], \n";
|
||||
for (int op = 0; op < opcodes.length; ++op)
|
||||
{
|
||||
int opcode = opcodes[op];
|
||||
modes ~= "[\"" ~ hexByte(opcode) ~ "\", \"";
|
||||
switch ((opcode & 0b00011100) >> 2)
|
||||
{
|
||||
case 0:
|
||||
modes ~= "Immediate";
|
||||
break;
|
||||
case 1:
|
||||
modes ~= "Zeropage()";
|
||||
break;
|
||||
case 3:
|
||||
modes ~= "Absolute()";
|
||||
break;
|
||||
case 5:
|
||||
modes ~= "Zeropage" ~ index ~ "()";
|
||||
break;
|
||||
case 7:
|
||||
modes ~= "Absolute" ~ index ~ "(" ~ type ~ ")";
|
||||
break;
|
||||
}
|
||||
modes ~= "\"]";
|
||||
if (op != (opcodes.length - 1)) modes ~= ", ";
|
||||
modes ~= "\n";
|
||||
}
|
||||
return modes ~ "]\n";
|
||||
}
|
||||
|
||||
static string Opcode(string[][] details, string action)
|
||||
{
|
||||
string methods;
|
||||
for (int op = 1; op < details.length; ++op)
|
||||
{
|
||||
methods ~= "override void opcode" ~ details[op][0] ~ "()\n{\n";
|
||||
if (details[op][1] == "Immediate")
|
||||
{
|
||||
methods ~= "primaryAddress = programCounter++;\n" ~
|
||||
action ~ "operand1 = readVal;\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
methods ~= "addr" ~ details[op][1] ~ ";\n" ~ action;
|
||||
}
|
||||
methods ~= "}\n";
|
||||
}
|
||||
return methods;
|
||||
}
|
||||
|
||||
static string Read(string action)
|
||||
{
|
||||
return UpdateNZ(action ~ " (readVal = readFinal(primaryAddress))");
|
||||
}
|
||||
|
||||
static string Decimal(string action)
|
||||
{
|
||||
string code = action ~ "(readVal = readFinal(primaryAddress));\n";
|
||||
return "if (flag.decimal) dec_" ~ code ~
|
||||
"else hex_" ~ code;
|
||||
}
|
||||
|
||||
static string Compare(string action)
|
||||
{
|
||||
return UpdateNZ("compare(" ~ action ~
|
||||
", (readVal = readFinal(primaryAddress)))");
|
||||
}
|
||||
|
||||
static string Write(string action)
|
||||
{
|
||||
return "writeFinal(primaryAddress, " ~ action ~ ");\n";
|
||||
}
|
||||
|
||||
static string BitTest()
|
||||
{
|
||||
return "bitTest(readVal = readFinal(primaryAddress));\n";
|
||||
}
|
||||
|
||||
mixin(SimpleOpcode("CLC", "18", "flag.carry = false"));
|
||||
mixin(SimpleOpcode("SEC", "38", "flag.carry = true"));
|
||||
mixin(SimpleOpcode("CLI", "58", "flag.interrupt = false"));
|
||||
mixin(SimpleOpcode("SEI", "78", "flag.interrupt = true"));
|
||||
mixin(SimpleOpcode("CLV", "B8", "flag.overflow = false"));
|
||||
mixin(SimpleOpcode("CLD", "D8", "flag.decimal = false"));
|
||||
mixin(SimpleOpcode("SED", "F8", "flag.decimal = true"));
|
||||
|
||||
mixin(SimpleOpcode("NOP", "EA", ""));
|
||||
|
||||
mixin(SimpleOpcode("PHP", "08", "push(flag.toByte())"));
|
||||
mixin(SimpleOpcode("PLP", "28", "flag.fromByte(pull())"));
|
||||
mixin(SimpleOpcode("PHA", "48", "push(accumulator)"));
|
||||
mixin(SimpleOpcode("TXS", "9A", "stackPointer = xIndex"));
|
||||
|
||||
mixin(RegisterOpcode("PLA", "68", "accumulator = pull()"));
|
||||
mixin(RegisterOpcode("TSX", "BA", "xIndex = stackPointer"));
|
||||
|
||||
mixin(RegisterOpcode("TAX", "AA", "xIndex = accumulator"));
|
||||
mixin(RegisterOpcode("TXA", "8A", "accumulator = xIndex"));
|
||||
mixin(RegisterOpcode("DEX", "CA", "xIndex -= 1"));
|
||||
mixin(RegisterOpcode("INX", "E8", "xIndex += 1"));
|
||||
mixin(RegisterOpcode("TAY", "A8", "yIndex = accumulator"));
|
||||
mixin(RegisterOpcode("TYA", "98", "accumulator = yIndex"));
|
||||
mixin(RegisterOpcode("DEY", "88", "yIndex -= 1"));
|
||||
mixin(RegisterOpcode("INY", "C8", "yIndex += 1"));
|
||||
|
||||
mixin(BranchOpcode("BPL", "10", "flag.negative_ < 0x80"));
|
||||
mixin(BranchOpcode("BMI", "30", "flag.negative_ > 0x7F"));
|
||||
mixin(BranchOpcode("BVC", "50", "!flag.overflow"));
|
||||
mixin(BranchOpcode("BVS", "70", "flag.overflow"));
|
||||
mixin(BranchOpcode("BCC", "90", "!flag.carry"));
|
||||
mixin(BranchOpcode("BCS", "B0", "flag.carry"));
|
||||
mixin(BranchOpcode("BNE", "D0", "flag.zero_ != 0"));
|
||||
mixin(BranchOpcode("BEQ", "F0", "flag.zero_ == 0"));
|
||||
|
||||
mixin(RegisterOpcode("ASL A", "0A",
|
||||
"accumulator = shiftLeft(accumulator)"));
|
||||
mixin(RegisterOpcode("ROL A", "2A",
|
||||
"accumulator = rotateLeft(accumulator)"));
|
||||
mixin(RegisterOpcode("LSR A", "4A",
|
||||
"accumulator = shiftRight(accumulator)"));
|
||||
mixin(RegisterOpcode("ROR A", "6A",
|
||||
"accumulator = rotateRight(accumulator)"));
|
||||
|
||||
mixin(Opcode(mixin(Type1Address(
|
||||
"LDA", "Read", [0xA1, 0xA5, 0xA9, 0xAD, 0xB1, 0xB5, 0xB9, 0xBD])),
|
||||
Read("accumulator =")));
|
||||
mixin(Opcode(mixin(Type1Address(
|
||||
"ORA", "Read", [0x01, 0x05, 0x09, 0x0D, 0x11, 0x15, 0x19, 0x1D])),
|
||||
Read("accumulator |=")));
|
||||
mixin(Opcode(mixin(Type1Address(
|
||||
"AND", "Read", [0x21, 0x25, 0x29, 0x2D, 0x31, 0x35, 0x39, 0x3D])),
|
||||
Read("accumulator &=")));
|
||||
mixin(Opcode(mixin(Type1Address(
|
||||
"EOR", "Read", [0x41, 0x45, 0x49, 0x4D, 0x51, 0x55, 0x59, 0x5D])),
|
||||
Read("accumulator ^=")));
|
||||
mixin(Opcode(mixin(Type1Address(
|
||||
"ADC", "Read", [0x61, 0x65, 0x69, 0x6D, 0x71, 0x75, 0x79, 0x7D])),
|
||||
Decimal("addWithCarry")));
|
||||
mixin(Opcode(mixin(Type1Address(
|
||||
"SBC", "Read", [0xE1, 0xE5, 0xE9, 0xED, 0xF1, 0xF5, 0xF9, 0xFD])),
|
||||
Decimal("subWithCarry")));
|
||||
mixin(Opcode(mixin(Type1Address(
|
||||
"CMP", "Read", [0xC1, 0xC5, 0xC9, 0xCD, 0xD1, 0xD5, 0xD9, 0xDD])),
|
||||
Compare("accumulator")));
|
||||
mixin(Opcode(mixin(Type1Address(
|
||||
"STA", "Write", [0x81, 0x85, 0x8D, 0x91, 0x95, 0x99, 0x9D])),
|
||||
Write("accumulator")));
|
||||
|
||||
mixin(Opcode(mixin(Type2Address(
|
||||
"LDX", "Read", [0xA2, 0xA6, 0xAE, 0xB6, 0xBE])),
|
||||
Read("xIndex =")));
|
||||
mixin(Opcode(mixin(Type2Address(
|
||||
"LDY", "Read", [0xA0, 0xA4, 0xAC, 0xB4, 0xBC])),
|
||||
Read("yIndex =")));
|
||||
mixin(Opcode(mixin(Type2Address(
|
||||
"CPX", "Read", [0xE0, 0xE4, 0xEC])),
|
||||
Compare("xIndex")));
|
||||
mixin(Opcode(mixin(Type2Address(
|
||||
"CPY", "Read", [0xC0, 0xC4, 0xCC])),
|
||||
Compare("yIndex")));
|
||||
mixin(Opcode(mixin(Type2Address(
|
||||
"STX", "Write", [0x86, 0x8E, 0x96])),
|
||||
Write("xIndex")));
|
||||
mixin(Opcode(mixin(Type2Address(
|
||||
"STY", "Write", [0x84, 0x8C, 0x94])),
|
||||
Write("yIndex")));
|
||||
mixin(Opcode(mixin(Type2Address(
|
||||
"BIT", "Read", [0x24, 0x2C])),
|
||||
BitTest()));
|
||||
|
||||
/* BRK */
|
||||
final override void opcode00()
|
||||
{
|
||||
peek(programCounter);
|
||||
++programCounter;
|
||||
doInterrupt(IRQ_VECTOR, flag.toByte());
|
||||
}
|
||||
|
||||
/* JSR */
|
||||
final override void opcode20()
|
||||
{
|
||||
ushort finalAddress = (operand1 = read(programCounter++));
|
||||
|
||||
peek(STACK_BASE + stackPointer);
|
||||
pushWord(programCounter);
|
||||
|
||||
finalAddress |= ((operand2 = read(programCounter)) << 8);
|
||||
version(CumulativeCycles) ticks(totalCycles);
|
||||
programCounter = finalAddress;
|
||||
}
|
||||
|
||||
/* RTI */
|
||||
final override void opcode40()
|
||||
{
|
||||
peek(programCounter);
|
||||
flag.fromByte(pull());
|
||||
programCounter = pullWord();
|
||||
version(CumulativeCycles) ticks(totalCycles);
|
||||
}
|
||||
|
||||
/* JMP $$$$ */
|
||||
final override void opcode4C()
|
||||
{
|
||||
programCounter = readWordOperand();
|
||||
version(CumulativeCycles) ticks(totalCycles);
|
||||
}
|
||||
|
||||
/* RTS */
|
||||