mirror of
https://github.com/edmccard/twoapple-reboot.git
synced 2024-12-26 08:31:14 +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 */
|
||||
final override void opcode60()
|
||||
{
|
||||
peek(programCounter);
|
||||
programCounter = pullWord();
|
||||
peek(programCounter);
|
||||
version(CumulativeCycles) ticks(totalCycles);
|
||||
++programCounter;
|
||||
}
|
||||
}
|
67
src/d6502/nmosbase.d
Normal file
67
src/d6502/nmosbase.d
Normal file
@ -0,0 +1,67 @@
|
||||
/+
|
||||
+ d6502/nmosbase.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.cpu;
|
||||
|
||||
class NmosBase : Cpu
|
||||
{
|
||||
this()
|
||||
{
|
||||
super();
|
||||
spuriousAddress = &badAddress;
|
||||
}
|
||||
|
||||
static string RMW(string action)
|
||||
{
|
||||
return "poke(primaryAddress, (readVal = read(primaryAddress)));\n" ~
|
||||
"writeFinal(primaryAddress, flag.zero_ = flag.negative_ = " ~
|
||||
action ~ "(readVal));\n";
|
||||
}
|
||||
|
||||
mixin(Opcode(mixin(Type2Address(
|
||||
"ASL", "Write", [0x06, 0x0E, 0x16, 0x1E])),
|
||||
RMW("shiftLeft")));
|
||||
mixin(Opcode(mixin(Type2Address(
|
||||
"LSR", "Write", [0x46, 0x4E, 0x56, 0x5E])),
|
||||
RMW("shiftRight")));
|
||||
mixin(Opcode(mixin(Type2Address(
|
||||
"ROL", "Write", [0x26, 0x2E, 0x36, 0x3E])),
|
||||
RMW("rotateLeft")));
|
||||
mixin(Opcode(mixin(Type2Address(
|
||||
"ROR", "Write", [0x66, 0x6E, 0x76, 0x7E])),
|
||||
RMW("rotateRight")));
|
||||
mixin(Opcode(mixin(Type2Address(
|
||||
"INC", "Write", [0xE6, 0xEE, 0xF6, 0xFE])),
|
||||
RMW("increment")));
|
||||
mixin(Opcode(mixin(Type2Address(
|
||||
"DEC", "Write", [0xC6, 0xCE, 0xD6, 0xDE])),
|
||||
RMW("decrement")));
|
||||
|
||||
/* JMP ($$$$) */
|
||||
override void opcode6C()
|
||||
{
|
||||
ushort vector = readWordOperand();
|
||||
programCounter = readWord(vector,
|
||||
(vector & 0xFF00) | cast(ubyte)(vector + 1));
|
||||
version(CumulativeCycles) ticks(totalCycles);
|
||||
}
|
||||
}
|
351
src/d6502/nmosundoc.d
Normal file
351
src/d6502/nmosundoc.d
Normal file
@ -0,0 +1,351 @@
|
||||
/+
|
||||
+ d6502/nmosundoc.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.nmosbase;
|
||||
|
||||
class NmosUndoc : NmosBase
|
||||
{
|
||||
this()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
final void addrHalt() {}
|
||||
final void addrImplied()
|
||||
{
|
||||
peek(programCounter);
|
||||
version(CumulativeCycles) ticks(totalCycles);
|
||||
}
|
||||
|
||||
final void strange(ubyte val)
|
||||
{
|
||||
version(Commodore64)
|
||||
{
|
||||
ubyte hiAddr = ((primaryAddress >> 8) + 1);
|
||||
val = val & hiAddr;
|
||||
ushort addr = (badAddress == primaryAddress) ? primaryAddress :
|
||||
((val << 8) | (primaryAddress & 0xFF));
|
||||
writeFinal(addr, val);
|
||||
}
|
||||
else
|
||||
{
|
||||
ubyte hiAddr = ((baseAddress >> 8) + 1);
|
||||
writeFinal(primaryAddress, val & hiAddr);
|
||||
}
|
||||
}
|
||||
|
||||
static string UndocAddress(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 3:
|
||||
modes ~= "Absolute()";
|
||||
break;
|
||||
case 4:
|
||||
modes ~= "IndirectY("~ type ~ ")";
|
||||
break;
|
||||
case 5:
|
||||
modes ~= "ZeropageX()";
|
||||
break;
|
||||
case 7:
|
||||
modes ~= "AbsoluteY(" ~ type ~ ")";
|
||||
break;
|
||||
}
|
||||
modes ~= "\"]";
|
||||
if (op != (opcodes.length - 1)) modes ~= ", ";
|
||||
modes ~= "\n";
|
||||
}
|
||||
return modes ~ "]\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";
|
||||
}
|
||||
|
||||
static string Halt()
|
||||
{
|
||||
/+ TODO: have this do something useful +/
|
||||
return "\n";
|
||||
}
|
||||
|
||||
static string ReadNOP()
|
||||
{
|
||||
return "readVal = readFinal(primaryAddress);\n";
|
||||
}
|
||||
|
||||
static string RMW_Read(string action1, string action2)
|
||||
{
|
||||
return "poke(primaryAddress, (readVal = read(primaryAddress)));\n" ~
|
||||
"writeFinal(primaryAddress, flag.zero_ = flag.negative_ = " ~
|
||||
"(writeVal = " ~ action1 ~ "(readVal)));\n" ~
|
||||
action2 ~ " writeVal;\n";
|
||||
}
|
||||
|
||||
static string RMW_Compare(string action1, string action2)
|
||||
{
|
||||
return "poke(primaryAddress, (readVal = read(primaryAddress)));\n" ~
|
||||
"writeFinal(primaryAddress, flag.zero_ = flag.negative_ = " ~
|
||||
"(writeVal = " ~ action1 ~ "(readVal)));\n" ~
|
||||
"compare(" ~ action2 ~ ", writeVal);\n";
|
||||
}
|
||||
|
||||
static string RMW_Decimal(string action1, string action2)
|
||||
{
|
||||
return "poke(primaryAddress, (readVal = read(primaryAddress)));\n" ~
|
||||
"writeFinal(primaryAddress, flag.zero_ = flag.negative_ = " ~
|
||||
"(writeVal = " ~ action1 ~ "(readVal)));\n" ~
|
||||
"if (flag.decimal) dec_" ~ action2 ~ "(writeVal);\n" ~
|
||||
"else hex_" ~ action2 ~ "(writeVal);\n";
|
||||
}
|
||||
|
||||
mixin(Opcode(mixin(ManualAddress(
|
||||
"HLT", [0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72, 0x92, 0xB2,
|
||||
0xD2, 0xF2], "Halt()")),
|
||||
Halt()));
|
||||
|
||||
mixin(Opcode(mixin(ManualAddress(
|
||||
"NOP", [0x1A, 0x3A, 0x5A, 0x7A, 0xDA, 0xFA], "Implied()")),
|
||||
""));
|
||||
mixin(Opcode(mixin(ManualAddress(
|
||||
"NOP", [0x0C, 0x1C, 0x3C, 0x5C, 0x7C, 0xDC, 0xFC],
|
||||
"AbsoluteX(false)")),
|
||||
ReadNOP()));
|
||||
mixin(Opcode(mixin(ManualAddress(
|
||||
"NOP", [0x80, 0x82, 0x89, 0xC2, 0xE2],
|
||||
"Immediate")),
|
||||
ReadNOP()));
|
||||
mixin(Opcode(mixin(ManualAddress(
|
||||
"NOP", [0x04, 0x44, 0x64],
|
||||
"Zeropage()")),
|
||||
ReadNOP()));
|
||||
mixin(Opcode(mixin(ManualAddress(
|
||||
"NOP", [0x14, 0x34, 0x54, 0x74, 0xD4, 0xF4],
|
||||
"ZeropageX()")),
|
||||
ReadNOP()));
|
||||
|
||||
mixin(Opcode(mixin(UndocAddress(
|
||||
"LAX", "Read", [0xA3, 0xA7, 0xAF, 0xB3, 0xB7, 0xBF])),
|
||||
Read("accumulator = xIndex =")));
|
||||
mixin(Opcode(mixin(UndocAddress(
|
||||
"SAX", "Write", [0x83, 0x87, 0x8F, 0x97])),
|
||||
Write("accumulator & xIndex")));
|
||||
|
||||
mixin(Opcode(mixin(Type1Address(
|
||||
"ASO", "Write", [0x03, 0x07, 0x0F, 0x13, 0x17, 0x1B, 0x1F])),
|
||||
RMW_Read("shiftLeft", "accumulator |=")));
|
||||
mixin(Opcode(mixin(Type1Address(
|
||||
"RLA", "Write", [0x23, 0x27, 0x2F, 0x33, 0x37, 0x3B, 0x3F])),
|
||||
RMW_Read("rotateLeft", "accumulator &=")));
|
||||
mixin(Opcode(mixin(Type1Address(
|
||||
"LSE", "Write", [0x43, 0x47, 0x4F, 0x53, 0x57, 0x5B, 0x5F])),
|
||||
RMW_Read("shiftRight", "accumulator ^=")));
|
||||
mixin(Opcode(mixin(Type1Address(
|
||||
"DCM", "Write", [0xC3, 0xC7, 0xCF, 0xD3, 0xD7, 0xDB, 0xDF])),
|
||||
RMW_Compare("decrement", "accumulator")));
|
||||
mixin(Opcode(mixin(Type1Address(
|
||||
"RRA", "Write", [0x63, 0x67, 0x6F, 0x73, 0x77, 0x7B, 0x7F])),
|
||||
RMW_Decimal("rotateRight", "addWithCarry")));
|
||||
mixin(Opcode(mixin(Type1Address(
|
||||
"INS", "Write", [0xE3, 0xE7, 0xEF, 0xF3, 0xF7, 0xFB, 0xFF])),
|
||||
RMW_Decimal("increment", "subWithCarry")));
|
||||
|
||||
/* ANC #$$ */
|
||||
override void opcode0B()
|
||||
{
|
||||
readVal = operand1 = readFinal(programCounter);
|
||||
flag.zero_ = flag.negative_ = (accumulator = readVal);
|
||||
flag.carry = (flag.negative_ > 0x7F);
|
||||
}
|
||||
|
||||
/* ANC #$$ */
|
||||
override void opcode2B()
|
||||
{
|
||||
readVal = operand1 = readFinal(programCounter);
|
||||
flag.zero_ = flag.negative_ = (accumulator = readVal);
|
||||
flag.carry = (flag.negative_ > 0x7F);
|
||||
}
|
||||
|
||||
/* ALR #$$ */
|
||||
override void opcode4B()
|
||||
{
|
||||
readVal = operand1 = readFinal(programCounter);
|
||||
flag.zero_ = flag.negative_ =
|
||||
(accumulator = shiftRight(accumulator & readVal));
|
||||
}
|
||||
|
||||
/* ARR #$$ */
|
||||
override void opcode6B()
|
||||
{
|
||||
readVal = operand1 = readFinal(programCounter);
|
||||
ubyte val = readVal & accumulator;
|
||||
if (flag.decimal) {
|
||||
ubyte temp = (val >> 1) + (flag.carry ? 0x80 : 0);
|
||||
flag.zero_ = flag.negative_ = temp;
|
||||
flag.overflow = (((temp ^ val) & 0x40) != 0);
|
||||
if ((readVal & 0x0F) + (val & 0x01) > 5)
|
||||
temp = (temp & 0xF0) + ((temp + 0x6) & 0x0F);
|
||||
if (val + (val & 0x10) >= 0x60)
|
||||
{
|
||||
temp += 0x60;
|
||||
flag.carry = true;
|
||||
}
|
||||
else
|
||||
flag.carry = false;
|
||||
accumulator = temp;
|
||||
}
|
||||
else {
|
||||
accumulator = (val >> 1) + (flag.carry ? 0x80 : 0);
|
||||
flag.zero_ = flag.negative_ = accumulator;
|
||||
val >>= 7;
|
||||
flag.carry = (val != 0);
|
||||
flag.overflow = ((val ^ ((accumulator >> 5) & 1)) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* ANE #$$ */
|
||||
override void opcode8B()
|
||||
{
|
||||
// unstable
|
||||
readVal = operand1 = readFinal(programCounter);
|
||||
|
||||
version(Atari8Bit)
|
||||
{
|
||||
flag.zero_ = flag.negative_ =
|
||||
(accumulator & xIndex & readVal);
|
||||
accumulator &= xIndex & (operand1 | 0xEF);
|
||||
}
|
||||
else
|
||||
{
|
||||
flag.zero_ = flag.negative_ =
|
||||
(accumulator &= (xIndex & readVal));
|
||||
}
|
||||
}
|
||||
|
||||
/* SHA ($$),Y */
|
||||
void opcode93()
|
||||
{
|
||||
addrIndirectY(true);
|
||||
strange(accumulator & xIndex);
|
||||
}
|
||||
|
||||
/* SHA $$$$,Y */
|
||||
void opcode9F()
|
||||
{
|
||||
addrAbsoluteY(true);
|
||||
strange(accumulator & xIndex);
|
||||
}
|
||||
|
||||
/* SHS $$$$,Y */
|
||||
void opcode9B()
|
||||
{
|
||||
addrAbsoluteY(true);
|
||||
strange(stackPointer = (accumulator & xIndex));
|
||||
}
|
||||
|
||||
/* SHY $$$$,X */
|
||||
void opcode9C()
|
||||
{
|
||||
addrAbsoluteX(true);
|
||||
strange(yIndex);
|
||||
}
|
||||
|
||||
/* SHX $$$$,Y */
|
||||
void opcode9E()
|
||||
{
|
||||
addrAbsoluteY(true);
|
||||
strange(xIndex);
|
||||
}
|
||||
|
||||
/* LAX #$$ */
|
||||
override void opcodeAB()
|
||||
{
|
||||
readVal = operand1 = readFinal(programCounter);
|
||||
|
||||
version(Commodore128)
|
||||
{
|
||||
// not unstable?
|
||||
flag.zero_ = flag.negative_ =
|
||||
(accumulator = readVal);
|
||||
}
|
||||
else
|
||||
{
|
||||
// unstable
|
||||
version(Commodore64)
|
||||
{
|
||||
accumulator |= 0xEE;
|
||||
}
|
||||
flag.zero_ = flag.negative_ =
|
||||
(accumulator &= readVal);
|
||||
}
|
||||
xIndex = accumulator;
|
||||
}
|
||||
|
||||
/* LAS $$$$,Y */
|
||||
override void opcodeBB()
|
||||
{
|
||||
addrAbsoluteY(false);
|
||||
readVal = readFinal(primaryAddress);
|
||||
|
||||
flag.zero_ = flag.negative_ =
|
||||
(xIndex = accumulator = (stackPointer & readVal));
|
||||
}
|
||||
|
||||
/* SBX #$$ */
|
||||
override void opcodeCB()
|
||||
{
|
||||
readVal = operand1 = readFinal(programCounter);
|
||||
xIndex &= accumulator;
|
||||
flag.zero_ = flag.negative_ = compare(xIndex, readVal);
|
||||
}
|
||||
|
||||
/* SBC #$$ */
|
||||
override void opcodeEB()
|
||||
{
|
||||
readVal = operand1 = readFinal(programCounter);
|
||||
if (flag.decimal) dec_subWithCarry(readVal);
|
||||
else hex_subWithCarry(readVal);
|
||||
}
|
||||
}
|
||||
|
89
src/data/charset_lower
Normal file
89
src/data/charset_lower
Normal file
@ -0,0 +1,89 @@
|
||||
[
|
||||
// ` a b c
|
||||
|
||||
0b0010000, 0b0000000, 0b0100000, 0b0000000,
|
||||
0b0001000, 0b0000000, 0b0100000, 0b0000000,
|
||||
0b0000100, 0b0011100, 0b0111100, 0b0011110,
|
||||
0b0000000, 0b0000010, 0b0100010, 0b0100000,
|
||||
0b0000000, 0b0011110, 0b0100010, 0b0100000,
|
||||
0b0000000, 0b0100010, 0b0100010, 0b0100000,
|
||||
0b0000000, 0b0011110, 0b0111100, 0b0011110,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
|
||||
// d e f g
|
||||
|
||||
0b0000010, 0b0000000, 0b0001100, 0b0000000,
|
||||
0b0000010, 0b0000000, 0b0010010, 0b0000000,
|
||||
0b0011110, 0b0011100, 0b0010000, 0b0011100,
|
||||
0b0100010, 0b0100010, 0b0111100, 0b0100010,
|
||||
0b0100010, 0b0111110, 0b0010000, 0b0100010,
|
||||
0b0100010, 0b0100000, 0b0010000, 0b0011110,
|
||||
0b0011110, 0b0011110, 0b0010000, 0b0000010,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0011100,
|
||||
|
||||
// h i j k
|
||||
|
||||
0b0100000, 0b0001000, 0b0000100, 0b0100000,
|
||||
0b0100000, 0b0000000, 0b0000000, 0b0100000,
|
||||
0b0111100, 0b0011000, 0b0001100, 0b0100010,
|
||||
0b0100010, 0b0001000, 0b0000100, 0b0100100,
|
||||
0b0100010, 0b0001000, 0b0000100, 0b0111000,
|
||||
0b0100010, 0b0001000, 0b0000100, 0b0100100,
|
||||
0b0100010, 0b0011100, 0b0100100, 0b0100010,
|
||||
0b0000000, 0b0000000, 0b0011000, 0b0000000,
|
||||
|
||||
// l m n o
|
||||
|
||||
0b0011000, 0b0000000, 0b0000000, 0b0000000,
|
||||
0b0001000, 0b0000000, 0b0000000, 0b0000000,
|
||||
0b0001000, 0b0110110, 0b0111100, 0b0011100,
|
||||
0b0001000, 0b0101010, 0b0100010, 0b0100010,
|
||||
0b0001000, 0b0101010, 0b0100010, 0b0100010,
|
||||
0b0001000, 0b0101010, 0b0100010, 0b0100010,
|
||||
0b0011100, 0b0100010, 0b0100010, 0b0011100,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
|
||||
// p q r s
|
||||
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
0b0111100, 0b0011110, 0b0101110, 0b0011110,
|
||||
0b0100010, 0b0100010, 0b0110000, 0b0100000,
|
||||
0b0100010, 0b0100010, 0b0100000, 0b0011100,
|
||||
0b0111100, 0b0011110, 0b0100000, 0b0000010,
|
||||
0b0100000, 0b0000010, 0b0100000, 0b0111100,
|
||||
0b0100000, 0b0000010, 0b0000000, 0b0000000,
|
||||
|
||||
// t u v w
|
||||
|
||||
0b0010000, 0b0000000, 0b0000000, 0b0000000,
|
||||
0b0010000, 0b0000000, 0b0000000, 0b0000000,
|
||||
0b0111100, 0b0100010, 0b0100010, 0b0100010,
|
||||
0b0010000, 0b0100010, 0b0100010, 0b0100010,
|
||||
0b0010000, 0b0100010, 0b0100010, 0b0101010,
|
||||
0b0010010, 0b0100110, 0b0010100, 0b0101010,
|
||||
0b0001100, 0b0011010, 0b0001000, 0b0110110,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
|
||||
// x y z {
|
||||
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0001110,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0011000,
|
||||
0b0100010, 0b0100010, 0b0111110, 0b0011000,
|
||||
0b0010100, 0b0100010, 0b0000100, 0b0110000,
|
||||
0b0001000, 0b0100010, 0b0001000, 0b0011000,
|
||||
0b0010100, 0b0011110, 0b0010000, 0b0011000,
|
||||
0b0100010, 0b0000010, 0b0111110, 0b0001110,
|
||||
0b0000000, 0b0011100, 0b0000000, 0b0000000,
|
||||
|
||||
// | } ~ (checker)
|
||||
|
||||
0b0001000, 0b0111000, 0b0011010, 0b0000000,
|
||||
0b0001000, 0b0001100, 0b0101100, 0b0101010,
|
||||
0b0001000, 0b0001100, 0b0000000, 0b0010100,
|
||||
0b0001000, 0b0000110, 0b0000000, 0b0101010,
|
||||
0b0001000, 0b0001100, 0b0000000, 0b0010100,
|
||||
0b0001000, 0b0001100, 0b0000000, 0b0101010,
|
||||
0b0001000, 0b0111000, 0b0000000, 0b0000000,
|
||||
0b0001000, 0b0000000, 0b0000000, 0b0000000
|
||||
];
|
91
src/data/charset_mouse
Normal file
91
src/data/charset_mouse
Normal file
@ -0,0 +1,91 @@
|
||||
[
|
||||
// derived from http://en.wikipedia.org/wiki/Image:Mousetext-original.png
|
||||
|
||||
// closed apple open apple pointer hourglass
|
||||
|
||||
0b0000100, 0b0000100, 0b0000000, 0b1111111,
|
||||
0b0001000, 0b0001000, 0b0000000, 0b0100010,
|
||||
0b0110110, 0b0110110, 0b0100000, 0b0010100,
|
||||
0b1111111, 0b1000001, 0b0110000, 0b0001000,
|
||||
0b1111110, 0b1000010, 0b0111000, 0b0001000,
|
||||
0b1111110, 0b1000010, 0b0111100, 0b0010100,
|
||||
0b0111111, 0b0101001, 0b0110110, 0b0101010,
|
||||
0b0110110, 0b0110110, 0b0100001, 0b1111111,
|
||||
|
||||
// check inv. check left man right man
|
||||
|
||||
0b0000000, 0b1111111, 0b0000111, 0b0000000,
|
||||
0b0000001, 0b1111110, 0b0000011, 0b0001100,
|
||||
0b0000010, 0b1111101, 0b0111111, 0b1110000,
|
||||
0b1000100, 0b0011011, 0b1000110, 0b0000000,
|
||||
0b0101000, 0b1010111, 0b1001111, 0b1110000,
|
||||
0b0010000, 0b1101111, 0b0000110, 0b0011000,
|
||||
0b0010000, 0b1101111, 0b1111110, 0b0001000,
|
||||
0b0000000, 0b1111111, 0b0100000, 0b0000111,
|
||||
|
||||
// left arrow ellipsis down arrow up arrow
|
||||
|
||||
0b0001000, 0b0000000, 0b0001000, 0b0001000,
|
||||
0b0010000, 0b0000000, 0b0001000, 0b0011100,
|
||||
0b0100000, 0b0000000, 0b0001000, 0b0101010,
|
||||
0b1111111, 0b0000000, 0b0001000, 0b1001001,
|
||||
0b0100000, 0b0000000, 0b1001001, 0b0001000,
|
||||
0b0010000, 0b0000000, 0b0101010, 0b0001000,
|
||||
0b0001000, 0b0000000, 0b0011100, 0b0001000,
|
||||
0b0000000, 0b0101010, 0b0001000, 0b0001000,
|
||||
|
||||
// top line enter block left arr. 2
|
||||
|
||||
0b1111111, 0b0000001, 0b1111110, 0b1100100,
|
||||
0b0000000, 0b0000001, 0b1111110, 0b0001100,
|
||||
0b0000000, 0b0000001, 0b1111110, 0b0011100,
|
||||
0b0000000, 0b0010001, 0b1111110, 0b0111111,
|
||||
0b0000000, 0b0110001, 0b1111110, 0b0011100,
|
||||
0b0000000, 0b1111111, 0b1111110, 0b0001100,
|
||||
0b0000000, 0b0110000, 0b1111110, 0b0000100,
|
||||
0b0000000, 0b0010000, 0b1111110, 0b1111011,
|
||||
|
||||
// right arr. 2 down arr. 2 up arr. 2 mid line
|
||||
|
||||
0b0010011, 0b0000001, 0b0000001, 0b0000000,
|
||||
0b0011000, 0b0001001, 0b0001001, 0b0000000,
|
||||
0b0011100, 0b0001000, 0b0011100, 0b0000000,
|
||||
0b1111110, 0b1111111, 0b0111110, 0b1111111,
|
||||
0b0011100, 0b0111110, 0b1111111, 0b0000000,
|
||||
0b0011000, 0b0011100, 0b0001000, 0b0000000,
|
||||
0b0010000, 0b0001001, 0b0001001, 0b0000000,
|
||||
0b1101111, 0b0000001, 0b0000001, 0b0000000,
|
||||
|
||||
// left/bot line right arrow checker 1 checker 2
|
||||
|
||||
0b1000000, 0b0001000, 0b0101010, 0b1010101,
|
||||
0b1000000, 0b0000100, 0b1010101, 0b0101010,
|
||||
0b1000000, 0b0000010, 0b0101010, 0b1010101,
|
||||
0b1000000, 0b1111111, 0b1010101, 0b0101010,
|
||||
0b1000000, 0b0000010, 0b0101010, 0b1010101,
|
||||
0b1000000, 0b0000100, 0b1010101, 0b0101010,
|
||||
0b1000000, 0b0001000, 0b0101010, 0b1010101,
|
||||
0b1111111, 0b0000000, 0b1010101, 0b0101010,
|
||||
|
||||
// left folder right folder right line diamond
|
||||
|
||||
0b0000000, 0b0000000, 0b0000001, 0b0001000,
|
||||
0b0111110, 0b0000000, 0b0000001, 0b0011100,
|
||||
0b1000001, 0b1111110, 0b0000001, 0b0111110,
|
||||
0b1000000, 0b0000001, 0b0000001, 0b1111111,
|
||||
0b1000000, 0b0000001, 0b0000001, 0b0111110,
|
||||
0b1000000, 0b0000001, 0b0000001, 0b0011100,
|
||||
0b1111111, 0b1111111, 0b0000001, 0b0001000,
|
||||
0b0000000, 0b0000000, 0b0000001, 0b0000000,
|
||||
|
||||
// top/bot line four corners close left line
|
||||
|
||||
0b1111111, 0b0010100, 0b1111111, 0b1000000,
|
||||
0b0000000, 0b0010100, 0b0000001, 0b1000000,
|
||||
0b0000000, 0b1110111, 0b0000001, 0b1000000,
|
||||
0b0000000, 0b0000000, 0b0011001, 0b1000000,
|
||||
0b0000000, 0b1110111, 0b0011001, 0b1000000,
|
||||
0b0000000, 0b0010100, 0b0000001, 0b1000000,
|
||||
0b0000000, 0b0010100, 0b0000001, 0b1000000,
|
||||
0b1111111, 0b0000000, 0b1111111, 0b1000000
|
||||
];
|
89
src/data/charset_symbol_ii
Normal file
89
src/data/charset_symbol_ii
Normal file
@ -0,0 +1,89 @@
|
||||
[
|
||||
// space ! " #
|
||||
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
0b0000000, 0b0001000, 0b0010100, 0b0010100,
|
||||
0b0000000, 0b0001000, 0b0010100, 0b0010100,
|
||||
0b0000000, 0b0001000, 0b0010100, 0b0111110,
|
||||
0b0000000, 0b0001000, 0b0000000, 0b0010100,
|
||||
0b0000000, 0b0001000, 0b0000000, 0b0111110,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0010100,
|
||||
0b0000000, 0b0001000, 0b0000000, 0b0010100,
|
||||
|
||||
// $ % & '
|
||||
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
0b0001000, 0b0110000, 0b0010000, 0b0001000,
|
||||
0b0011110, 0b0110010, 0b0101000, 0b0001000,
|
||||
0b0101000, 0b0000100, 0b0101000, 0b0001000,
|
||||
0b0011100, 0b0001000, 0b0010000, 0b0000000,
|
||||
0b0001010, 0b0010000, 0b0101010, 0b0000000,
|
||||
0b0111100, 0b0100110, 0b0100100, 0b0000000,
|
||||
0b0001000, 0b0000110, 0b0011010, 0b0000000,
|
||||
|
||||
// ( ) * +
|
||||
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
0b0001000, 0b0001000, 0b0001000, 0b0000000,
|
||||
0b0010000, 0b0000100, 0b0101010, 0b0001000,
|
||||
0b0100000, 0b0000010, 0b0011100, 0b0001000,
|
||||
0b0100000, 0b0000010, 0b0001000, 0b0111110,
|
||||
0b0100000, 0b0000010, 0b0011100, 0b0001000,
|
||||
0b0010000, 0b0000100, 0b0101010, 0b0001000,
|
||||
0b0001000, 0b0001000, 0b0001000, 0b0000000,
|
||||
|
||||
// 0, - . /
|
||||
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000010,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000100,
|
||||
0b0000000, 0b0111110, 0b0000000, 0b0001000,
|
||||
0b0001000, 0b0000000, 0b0000000, 0b0010000,
|
||||
0b0001000, 0b0000000, 0b0000000, 0b0100000,
|
||||
0b0010000, 0b0000000, 0b0001000, 0b0000000,
|
||||
|
||||
// 0 1 2 3
|
||||
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
0b0011100, 0b0001000, 0b0011100, 0b0111110,
|
||||
0b0100010, 0b0011000, 0b0100010, 0b0000010,
|
||||
0b0100110, 0b0001000, 0b0000010, 0b0000100,
|
||||
0b0101010, 0b0001000, 0b0001100, 0b0001100,
|
||||
0b0110010, 0b0001000, 0b0010000, 0b0000010,
|
||||
0b0100010, 0b0001000, 0b0100000, 0b0100010,
|
||||
0b0011100, 0b0011100, 0b0111110, 0b0011100,
|
||||
|
||||
// 4 5 6 7
|
||||
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
0b0000100, 0b0111110, 0b0001110, 0b0111110,
|
||||
0b0001100, 0b0100000, 0b0010000, 0b0000010,
|
||||
0b0010100, 0b0111100, 0b0100000, 0b0000100,
|
||||
0b0100100, 0b0000010, 0b0111100, 0b0001000,
|
||||
0b0111110, 0b0000010, 0b0100010, 0b0010000,
|
||||
0b0000100, 0b0100010, 0b0100010, 0b0010000,
|
||||
0b0000100, 0b0011100, 0b0011100, 0b0010000,
|
||||
|
||||
// 8 9 : ;
|
||||
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
0b0011100, 0b0011100, 0b0000000, 0b0000000,
|
||||
0b0100010, 0b0100010, 0b0000000, 0b0000000,
|
||||
0b0100010, 0b0100010, 0b0001000, 0b0001000,
|
||||
0b0011100, 0b0011110, 0b0000000, 0b0000000,
|
||||
0b0100010, 0b0000010, 0b0001000, 0b0001000,
|
||||
0b0100010, 0b0000100, 0b0000000, 0b0001000,
|
||||
0b0011100, 0b0111000, 0b0000000, 0b0010000,
|
||||
|
||||
// < = > ?
|
||||
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
0b0000100, 0b0000000, 0b0010000, 0b0011100,
|
||||
0b0001000, 0b0000000, 0b0001000, 0b0100010,
|
||||
0b0010000, 0b0111110, 0b0000100, 0b0000100,
|
||||
0b0100000, 0b0000000, 0b0000010, 0b0001000,
|
||||
0b0010000, 0b0111110, 0b0000100, 0b0001000,
|
||||
0b0001000, 0b0000000, 0b0001000, 0b0000000,
|
||||
0b0000100, 0b0000000, 0b0010000, 0b0001000
|
||||
];
|
89
src/data/charset_symbol_iie
Normal file
89
src/data/charset_symbol_iie
Normal file
@ -0,0 +1,89 @@
|
||||
[
|
||||
// space ! " #
|
||||
|
||||
0b0000000, 0b0001000, 0b0010100, 0b0010100,
|
||||
0b0000000, 0b0001000, 0b0010100, 0b0010100,
|
||||
0b0000000, 0b0001000, 0b0010100, 0b0111110,
|
||||
0b0000000, 0b0001000, 0b0000000, 0b0010100,
|
||||
0b0000000, 0b0001000, 0b0000000, 0b0111110,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0010100,
|
||||
0b0000000, 0b0001000, 0b0000000, 0b0010100,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
|
||||
// $ % & '
|
||||
|
||||
0b0001000, 0b0110000, 0b0010000, 0b0001000,
|
||||
0b0011110, 0b0110010, 0b0101000, 0b0001000,
|
||||
0b0101000, 0b0000100, 0b0101000, 0b0001000,
|
||||
0b0011100, 0b0001000, 0b0010000, 0b0000000,
|
||||
0b0001010, 0b0010000, 0b0101010, 0b0000000,
|
||||
0b0111100, 0b0100110, 0b0100100, 0b0000000,
|
||||
0b0001000, 0b0000110, 0b0011010, 0b0000000,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
|
||||
// ( ) * +
|
||||
|
||||
0b0001000, 0b0001000, 0b0001000, 0b0000000,
|
||||
0b0010000, 0b0000100, 0b0101010, 0b0001000,
|
||||
0b0100000, 0b0000010, 0b0011100, 0b0001000,
|
||||
0b0100000, 0b0000010, 0b0001000, 0b0111110,
|
||||
0b0100000, 0b0000010, 0b0011100, 0b0001000,
|
||||
0b0010000, 0b0000100, 0b0101010, 0b0001000,
|
||||
0b0001000, 0b0001000, 0b0001000, 0b0000000,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
|
||||
// 0, - . /
|
||||
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000010,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000100,
|
||||
0b0000000, 0b0111110, 0b0000000, 0b0001000,
|
||||
0b0001000, 0b0000000, 0b0000000, 0b0010000,
|
||||
0b0001000, 0b0000000, 0b0000000, 0b0100000,
|
||||
0b0010000, 0b0000000, 0b0001000, 0b0000000,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
|
||||
// 0 1 2 3
|
||||
|
||||
0b0011100, 0b0001000, 0b0011100, 0b0111110,
|
||||
0b0100010, 0b0011000, 0b0100010, 0b0000010,
|
||||
0b0100110, 0b0001000, 0b0000010, 0b0000100,
|
||||
0b0101010, 0b0001000, 0b0001100, 0b0001100,
|
||||
0b0110010, 0b0001000, 0b0010000, 0b0000010,
|
||||
0b0100010, 0b0001000, 0b0100000, 0b0100010,
|
||||
0b0011100, 0b0011100, 0b0111110, 0b0011100,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
|
||||
// 4 5 6 7
|
||||
|
||||
0b0000100, 0b0111110, 0b0001110, 0b0111110,
|
||||
0b0001100, 0b0100000, 0b0010000, 0b0000010,
|
||||
0b0010100, 0b0111100, 0b0100000, 0b0000100,
|
||||
0b0100100, 0b0000010, 0b0111100, 0b0001000,
|
||||
0b0111110, 0b0000010, 0b0100010, 0b0010000,
|
||||
0b0000100, 0b0100010, 0b0100010, 0b0010000,
|
||||
0b0000100, 0b0011100, 0b0011100, 0b0010000,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
|
||||
// 8 9 : ;
|
||||
|
||||
0b0011100, 0b0011100, 0b0000000, 0b0000000,
|
||||
0b0100010, 0b0100010, 0b0000000, 0b0000000,
|
||||
0b0100010, 0b0100010, 0b0001000, 0b0001000,
|
||||
0b0011100, 0b0011110, 0b0000000, 0b0000000,
|
||||
0b0100010, 0b0000010, 0b0001000, 0b0001000,
|
||||
0b0100010, 0b0000100, 0b0000000, 0b0001000,
|
||||
0b0011100, 0b0111000, 0b0000000, 0b0010000,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
|
||||
// < = > ?
|
||||
|
||||
0b0000100, 0b0000000, 0b0010000, 0b0011100,
|
||||
0b0001000, 0b0000000, 0b0001000, 0b0100010,
|
||||
0b0010000, 0b0111110, 0b0000100, 0b0000100,
|
||||
0b0100000, 0b0000000, 0b0000010, 0b0001000,
|
||||
0b0010000, 0b0111110, 0b0000100, 0b0001000,
|
||||
0b0001000, 0b0000000, 0b0001000, 0b0000000,
|
||||
0b0000100, 0b0000000, 0b0010000, 0b0001000,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000
|
||||
];
|
90
src/data/charset_upper_ii
Normal file
90
src/data/charset_upper_ii
Normal file
@ -0,0 +1,90 @@
|
||||
[
|
||||
// @ A B C
|
||||
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
0b0011100, 0b0001000, 0b0111100, 0b0011100,
|
||||
0b0100010, 0b0010100, 0b0100010, 0b0100010,
|
||||
0b0101010, 0b0100010, 0b0100010, 0b0100000,
|
||||
0b0101110, 0b0100010, 0b0111100, 0b0100000,
|
||||
0b0101100, 0b0111110, 0b0100010, 0b0100000,
|
||||
0b0100000, 0b0100010, 0b0100010, 0b0100010,
|
||||
0b0011110, 0b0100010, 0b0111100, 0b0011100,
|
||||
|
||||
// D E F G
|
||||
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
0b0111100, 0b0111110, 0b0111110, 0b0011110,
|
||||
0b0100010, 0b0100000, 0b0100000, 0b0100000,
|
||||
0b0100010, 0b0100000, 0b0100000, 0b0100000,
|
||||
0b0100010, 0b0111100, 0b0111100, 0b0100000,
|
||||
0b0100010, 0b0100000, 0b0100000, 0b0100110,
|
||||
0b0100010, 0b0100000, 0b0100000, 0b0100010,
|
||||
0b0111100, 0b0111110, 0b0100000, 0b0011110,
|
||||
|
||||
// H I J K
|
||||
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
0b0100010, 0b0011100, 0b0000010, 0b0100010,
|
||||
0b0100010, 0b0001000, 0b0000010, 0b0100100,
|
||||
0b0100010, 0b0001000, 0b0000010, 0b0101000,
|
||||
0b0111110, 0b0001000, 0b0000010, 0b0110000,
|
||||
0b0100010, 0b0001000, 0b0000010, 0b0101000,
|
||||
0b0100010, 0b0001000, 0b0100010, 0b0100100,
|
||||
0b0100010, 0b0011100, 0b0011100, 0b0100010,
|
||||
|
||||
// L M N O
|
||||
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
0b0100000, 0b0100010, 0b0100010, 0b0011100,
|
||||
0b0100000, 0b0110110, 0b0100010, 0b0100010,
|
||||
0b0100000, 0b0101010, 0b0110010, 0b0100010,
|
||||
0b0100000, 0b0101010, 0b0101010, 0b0100010,
|
||||
0b0100000, 0b0100010, 0b0100110, 0b0100010,
|
||||
0b0100000, 0b0100010, 0b0100010, 0b0100010,
|
||||
0b0111110, 0b0100010, 0b0100010, 0b0011100,
|
||||
|
||||
// P Q R S
|
||||
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
0b0111100, 0b0011100, 0b0111100, 0b0011100,
|
||||
0b0100010, 0b0100010, 0b0100010, 0b0100010,
|
||||
0b0100010, 0b0100010, 0b0100010, 0b0100000,
|
||||
0b0111100, 0b0100010, 0b0111100, 0b0011100,
|
||||
0b0100000, 0b0101010, 0b0101000, 0b0000010,
|
||||
0b0100000, 0b0100100, 0b0100100, 0b0100010,
|
||||
0b0100000, 0b0011010, 0b0100010, 0b0011100,
|
||||
|
||||
// T U V W
|
||||
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
0b0111110, 0b0100010, 0b0100010, 0b0100010,
|
||||
0b0001000, 0b0100010, 0b0100010, 0b0100010,
|
||||
0b0001000, 0b0100010, 0b0100010, 0b0100010,
|
||||
0b0001000, 0b0100010, 0b0100010, 0b0101010,
|
||||
0b0001000, 0b0100010, 0b0100010, 0b0101010,
|
||||
0b0001000, 0b0100010, 0b0010100, 0b0110110,
|
||||
0b0001000, 0b0011100, 0b0001000, 0b0100010,
|
||||
|
||||
// X Y Z [
|
||||
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
0b0100010, 0b0100010, 0b0111110, 0b0111110,
|
||||
0b0100010, 0b0100010, 0b0000010, 0b0110000,
|
||||
0b0010100, 0b0010100, 0b0000100, 0b0110000,
|
||||
0b0001000, 0b0001000, 0b0001000, 0b0110000,
|
||||
0b0010100, 0b0001000, 0b0010000, 0b0110000,
|
||||
0b0100010, 0b0001000, 0b0100000, 0b0110000,
|
||||
0b0100010, 0b0001000, 0b0111110, 0b0111110,
|
||||
|
||||
// \ ] ^ _
|
||||
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
0b0000000, 0b0111110, 0b0000000, 0b0000000,
|
||||
0b0100000, 0b0000110, 0b0000000, 0b0000000,
|
||||
0b0010000, 0b0000110, 0b0001000, 0b0000000,
|
||||
0b0001000, 0b0000110, 0b0010100, 0b0000000,
|
||||
0b0000100, 0b0000110, 0b0100010, 0b0000000,
|
||||
0b0000010, 0b0000110, 0b0000000, 0b0000000,
|
||||
0b0000000, 0b0111110, 0b0000000, 0b0111110
|
||||
];
|
||||
|
90
src/data/charset_upper_iie
Normal file
90
src/data/charset_upper_iie
Normal file
@ -0,0 +1,90 @@
|
||||
[
|
||||
// @ A B C
|
||||
|
||||
0b0011100, 0b0001000, 0b0111100, 0b0011100,
|
||||
0b0100010, 0b0010100, 0b0100010, 0b0100010,
|
||||
0b0101010, 0b0100010, 0b0100010, 0b0100000,
|
||||
0b0101110, 0b0100010, 0b0111100, 0b0100000,
|
||||
0b0101100, 0b0111110, 0b0100010, 0b0100000,
|
||||
0b0100000, 0b0100010, 0b0100010, 0b0100010,
|
||||
0b0011110, 0b0100010, 0b0111100, 0b0011100,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
|
||||
// D E F G
|
||||
|
||||
0b0111100, 0b0111110, 0b0111110, 0b0011110,
|
||||
0b0100010, 0b0100000, 0b0100000, 0b0100000,
|
||||
0b0100010, 0b0100000, 0b0100000, 0b0100000,
|
||||
0b0100010, 0b0111100, 0b0111100, 0b0100000,
|
||||
0b0100010, 0b0100000, 0b0100000, 0b0100110,
|
||||
0b0100010, 0b0100000, 0b0100000, 0b0100010,
|
||||
0b0111100, 0b0111110, 0b0100000, 0b0011110,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
|
||||
// H I J K
|
||||
|
||||
0b0100010, 0b0011100, 0b0000010, 0b0100010,
|
||||
0b0100010, 0b0001000, 0b0000010, 0b0100100,
|
||||
0b0100010, 0b0001000, 0b0000010, 0b0101000,
|
||||
0b0111110, 0b0001000, 0b0000010, 0b0110000,
|
||||
0b0100010, 0b0001000, 0b0000010, 0b0101000,
|
||||
0b0100010, 0b0001000, 0b0100010, 0b0100100,
|
||||
0b0100010, 0b0011100, 0b0011100, 0b0100010,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
|
||||
// L M N O
|
||||
|
||||
0b0100000, 0b0100010, 0b0100010, 0b0011100,
|
||||
0b0100000, 0b0110110, 0b0100010, 0b0100010,
|
||||
0b0100000, 0b0101010, 0b0110010, 0b0100010,
|
||||
0b0100000, 0b0101010, 0b0101010, 0b0100010,
|
||||
0b0100000, 0b0100010, 0b0100110, 0b0100010,
|
||||
0b0100000, 0b0100010, 0b0100010, 0b0100010,
|
||||
0b0111110, 0b0100010, 0b0100010, 0b0011100,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
|
||||
// P Q R S
|
||||
|
||||
0b0111100, 0b0011100, 0b0111100, 0b0011100,
|
||||
0b0100010, 0b0100010, 0b0100010, 0b0100010,
|
||||
0b0100010, 0b0100010, 0b0100010, 0b0100000,
|
||||
0b0111100, 0b0100010, 0b0111100, 0b0011100,
|
||||
0b0100000, 0b0101010, 0b0101000, 0b0000010,
|
||||
0b0100000, 0b0100100, 0b0100100, 0b0100010,
|
||||
0b0100000, 0b0011010, 0b0100010, 0b0011100,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
|
||||
// T U V W
|
||||
|
||||
0b0111110, 0b0100010, 0b0100010, 0b0100010,
|
||||
0b0001000, 0b0100010, 0b0100010, 0b0100010,
|
||||
0b0001000, 0b0100010, 0b0100010, 0b0100010,
|
||||
0b0001000, 0b0100010, 0b0100010, 0b0101010,
|
||||
0b0001000, 0b0100010, 0b0100010, 0b0101010,
|
||||
0b0001000, 0b0100010, 0b0010100, 0b0110110,
|
||||
0b0001000, 0b0011100, 0b0001000, 0b0100010,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
|
||||
// X Y Z [
|
||||
|
||||
0b0100010, 0b0100010, 0b0111110, 0b0111110,
|
||||
0b0100010, 0b0100010, 0b0000010, 0b0110000,
|
||||
0b0010100, 0b0010100, 0b0000100, 0b0110000,
|
||||
0b0001000, 0b0001000, 0b0001000, 0b0110000,
|
||||
0b0010100, 0b0001000, 0b0010000, 0b0110000,
|
||||
0b0100010, 0b0001000, 0b0100000, 0b0110000,
|
||||
0b0100010, 0b0001000, 0b0111110, 0b0111110,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b0000000,
|
||||
|
||||
// \ ] ^ _
|
||||
|
||||
0b0000000, 0b0111110, 0b0000000, 0b0000000,
|
||||
0b0100000, 0b0000110, 0b0000000, 0b0000000,
|
||||
0b0010000, 0b0000110, 0b0001000, 0b0000000,
|
||||
0b0001000, 0b0000110, 0b0010100, 0b0000000,
|
||||
0b0000100, 0b0000110, 0b0100010, 0b0000000,
|
||||
0b0000010, 0b0000110, 0b0000000, 0b0000000,
|
||||
0b0000000, 0b0111110, 0b0000000, 0b0000000,
|
||||
0b0000000, 0b0000000, 0b0000000, 0b1111111
|
||||
];
|
||||
|
BIN
src/data/hires80_test
Normal file
BIN
src/data/hires80_test
Normal file
Binary file not shown.
BIN
src/data/hires_test
Normal file
BIN
src/data/hires_test
Normal file
Binary file not shown.
442
src/data/laz_scheme
Normal file
442
src/data/laz_scheme
Normal file
@ -0,0 +1,442 @@
|
||||
[
|
||||
// Ellsworth
|
||||
[
|
||||
// Ellsworth:A2_ColorSet_DARK_BLUE
|
||||
[
|
||||
Color.Black,
|
||||
Color.Brown,
|
||||
Color.DarkGreen,
|
||||
Color.Green,
|
||||
Color.DarkBlue,
|
||||
Color.LightGray,
|
||||
Color.MediumBlue,
|
||||
Color.Aqua,
|
||||
Color.Red,
|
||||
Color.Orange,
|
||||
Color.DarkGray,
|
||||
Color.Yellow,
|
||||
Color.Purple,
|
||||
Color.Pink,
|
||||
Color.LightBlue,
|
||||
Color.White,
|
||||
Color.Brown,
|
||||
Color.Brown,
|
||||
Color.Green,
|
||||
Color.Green,
|
||||
Color.LightGray,
|
||||
Color.LightGray,
|
||||
Color.Aqua,
|
||||
Color.Aqua,
|
||||
Color.Orange,
|
||||
Color.Orange,
|
||||
Color.Yellow,
|
||||
Color.Yellow,
|
||||
Color.Pink,
|
||||
Color.Pink,
|
||||
Color.White,
|
||||
Color.White
|
||||
],
|
||||
|
||||
// Ellsworth:A2_ColorSet_DARK_GREEN
|
||||
[
|
||||
Color.Black,
|
||||
Color.Red,
|
||||
Color.Brown,
|
||||
Color.Orange,
|
||||
Color.DarkGreen,
|
||||
Color.DarkGray,
|
||||
Color.Green,
|
||||
Color.Yellow,
|
||||
Color.DarkBlue,
|
||||
Color.Purple,
|
||||
Color.LightGray,
|
||||
Color.Pink,
|
||||
Color.MediumBlue,
|
||||
Color.LightBlue,
|
||||
Color.Aqua,
|
||||
Color.White,
|
||||
Color.Red,
|
||||
Color.Red,
|
||||
Color.Orange,
|
||||
Color.Orange,
|
||||
Color.DarkGray,
|
||||
Color.DarkGray,
|
||||
Color.Yellow,
|
||||
Color.Yellow,
|
||||
Color.Purple,
|
||||
Color.Purple,
|
||||
Color.Pink,
|
||||
Color.Pink,
|
||||
Color.LightBlue,
|
||||
Color.LightBlue,
|
||||
Color.White,
|
||||
Color.White
|
||||
],
|
||||
|
||||
// Ellsworth:A2_ColorSet_BROWN
|
||||
[
|
||||
Color.Black,
|
||||
Color.DarkBlue,
|
||||
Color.Red,
|
||||
Color.Purple,
|
||||
Color.Brown,
|
||||
Color.LightGray,
|
||||
Color.Orange,
|
||||
Color.Pink,
|
||||
Color.DarkGreen,
|
||||
Color.MediumBlue,
|
||||
Color.DarkGray,
|
||||
Color.LightBlue,
|
||||
Color.Green,
|
||||
Color.Aqua,
|
||||
Color.Yellow,
|
||||
Color.White,
|
||||
Color.DarkBlue,
|
||||
Color.DarkBlue,
|
||||
Color.Purple,
|
||||
Color.Purple,
|
||||
Color.LightGray,
|
||||
Color.LightGray,
|
||||
Color.Pink,
|
||||
Color.Pink,
|
||||
Color.MediumBlue,
|
||||
Color.MediumBlue,
|
||||
Color.LightBlue,
|
||||
Color.LightBlue,
|
||||
Color.Aqua,
|
||||
Color.Aqua,
|
||||
Color.White,
|
||||
Color.White
|
||||
],
|
||||
|
||||
// Ellsworth:A2_ColorSet_RED
|
||||
[
|
||||
Color.Black,
|
||||
Color.DarkGreen,
|
||||
Color.DarkBlue,
|
||||
Color.MediumBlue,
|
||||
Color.Red,
|
||||
Color.DarkGray,
|
||||
Color.Purple,
|
||||
Color.LightBlue,
|
||||
Color.Brown,
|
||||
Color.Green,
|
||||
Color.LightGray,
|
||||
Color.Aqua,
|
||||
Color.Orange,
|
||||
Color.Yellow,
|
||||
Color.Pink,
|
||||
Color.White,
|
||||
Color.DarkGreen,
|
||||
Color.DarkGreen,
|
||||
Color.MediumBlue,
|
||||
Color.MediumBlue,
|
||||
Color.DarkGray,
|
||||
Color.DarkGray,
|
||||
Color.LightBlue,
|
||||
Color.LightBlue,
|
||||
Color.Green,
|
||||
Color.Green,
|
||||
Color.Aqua,
|
||||
Color.Aqua,
|
||||
Color.Yellow,
|
||||
Color.Yellow,
|
||||
Color.White,
|
||||
Color.White
|
||||
]
|
||||
],
|
||||
|
||||
// DoubleColor
|
||||
[
|
||||
// DoubleColor:A2_ColorSet_DARK_BLUE
|
||||
[
|
||||
Color.Black,
|
||||
Color.Black,
|
||||
Color.DarkGreen + 0x10,
|
||||
Color.Green + 0x10,
|
||||
Color.DarkBlue,
|
||||
Color.LightGray,
|
||||
Color.MediumBlue,
|
||||
Color.Aqua,
|
||||
Color.Red + 0x10,
|
||||
Color.Orange + 0x10,
|
||||
Color.DarkGray,
|
||||
Color.Yellow + 0x10,
|
||||
Color.Purple,
|
||||
Color.Pink,
|
||||
Color.LightBlue,
|
||||
Color.White,
|
||||
Color.Black,
|
||||
Color.Brown + 0x10,
|
||||
Color.Green + 0x10,
|
||||
Color.Green + 0x10,
|
||||
Color.LightGray,
|
||||
Color.LightGray,
|
||||
Color.Aqua,
|
||||
Color.Aqua,
|
||||
Color.Orange + 0x10,
|
||||
Color.Orange + 0x10,
|
||||
Color.Yellow + 0x10,
|
||||
Color.Yellow + 0x10,
|
||||
Color.Pink,
|
||||
Color.Pink,
|
||||
Color.White,
|
||||
Color.White
|
||||
],
|
||||
|
||||
// DoubleColor:A2_ColorSet_DARK_GREEN
|
||||
[
|
||||
Color.Black,
|
||||
Color.Black,
|
||||
Color.Brown + 0x10,
|
||||
Color.Orange + 0x10,
|
||||
Color.DarkGreen,
|
||||
Color.DarkGray,
|
||||
Color.Green,
|
||||
Color.Yellow,
|
||||
Color.DarkBlue + 0x10,
|
||||
Color.Purple + 0x10,
|
||||
Color.LightGray,
|
||||
Color.Pink + 0x10,
|
||||
Color.MediumBlue,
|
||||
Color.LightBlue,
|
||||
Color.Aqua,
|
||||
Color.White,
|
||||
Color.Black,
|
||||
Color.Red + 0x10,
|
||||
Color.Orange + 0x10,
|
||||
Color.Orange + 0x10,
|
||||
Color.DarkGray,
|
||||
Color.DarkGray,
|
||||
Color.Yellow,
|
||||
Color.Yellow,
|
||||
Color.Purple + 0x10,
|
||||
Color.Purple + 0x10,
|
||||
Color.Pink + 0x10,
|
||||
Color.Pink + 0x10,
|
||||
Color.LightBlue,
|
||||
Color.LightBlue,
|
||||
Color.White,
|
||||
Color.White
|
||||
],
|
||||
|
||||
// DoubleColor:A2_ColorSet_BROWN
|
||||
[
|
||||
Color.Black,
|
||||
Color.Black,
|
||||
Color.Red + 0x10,
|
||||
Color.Purple + 0x10,
|
||||
Color.Brown,
|
||||
Color.LightGray,
|
||||
Color.Orange,
|
||||
Color.Pink,
|
||||
Color.DarkGreen + 0x10,
|
||||
Color.MediumBlue + 0x10,
|
||||
Color.DarkGray,
|
||||
Color.LightBlue + 0x10,
|
||||
Color.Green,
|
||||
Color.Aqua,
|
||||
Color.Yellow,
|
||||
Color.White,
|
||||
Color.Black,
|
||||
Color.DarkBlue + 0x10,
|
||||
Color.Purple + 0x10,
|
||||
Color.Purple + 0x10,
|
||||
Color.LightGray,
|
||||
Color.LightGray,
|
||||
Color.Pink,
|
||||
Color.Pink,
|
||||
Color.MediumBlue + 0x10,
|
||||
Color.MediumBlue + 0x10,
|
||||
Color.LightBlue + 0x10,
|
||||
Color.LightBlue + 0x10,
|
||||
Color.Aqua,
|
||||
Color.Aqua,
|
||||
Color.White,
|
||||
Color.White
|
||||
],
|
||||
|
||||
// DoubleColor:A2_ColorSet_RED
|
||||
[
|
||||
Color.Black,
|
||||
Color.Black,
|
||||
Color.DarkBlue + 0x10,
|
||||
Color.MediumBlue + 0x10,
|
||||
Color.Red,
|
||||
Color.DarkGray,
|
||||
Color.Purple,
|
||||
Color.LightBlue,
|
||||
Color.Brown + 0x10,
|
||||
Color.Green + 0x10,
|
||||
Color.LightGray,
|
||||
Color.Aqua + 0x10,
|
||||
Color.Orange,
|
||||
Color.Yellow,
|
||||
Color.Pink,
|
||||
Color.White,
|
||||
Color.Black,
|
||||
Color.DarkGreen + 0x10,
|
||||
Color.MediumBlue + 0x10,
|
||||
Color.MediumBlue + 0x10,
|
||||
Color.DarkGray,
|
||||
Color.DarkGray,
|
||||
Color.LightBlue,
|
||||
Color.LightBlue,
|
||||
Color.Green + 0x10,
|
||||
Color.Green + 0x10,
|
||||
Color.Aqua + 0x10,
|
||||
Color.Aqua + 0x10,
|
||||
Color.Yellow,
|
||||
Color.Yellow,
|
||||
Color.White,
|
||||
Color.White
|
||||
]
|
||||
],
|
||||
|
||||
// Sharp
|
||||
[
|
||||
// Sharp:A2_ColorSet_DARK_BLUE
|
||||
[
|
||||
Color.Black,
|
||||
Color.Black,
|
||||
Color.DarkGreen + 0x80,
|
||||
Color.Green + 0x80,
|
||||
Color.DarkBlue,
|
||||
Color.LightGray,
|
||||
Color.MediumBlue,
|
||||
Color.Aqua + 0x80,
|
||||
Color.Red + 0x80,
|
||||
Color.Orange,
|
||||
Color.DarkGray,
|
||||
Color.Yellow,
|
||||
Color.Purple,
|
||||
Color.Pink,
|
||||
Color.LightBlue,
|
||||
Color.White,
|
||||
Color.Black,
|
||||
Color.Brown + 0x80,
|
||||
Color.Green,
|
||||
Color.Green,
|
||||
Color.LightGray,
|
||||
Color.LightGray,
|
||||
Color.Aqua,
|
||||
Color.Aqua,
|
||||
Color.Orange + 0x80,
|
||||
Color.Orange,
|
||||
Color.Yellow,
|
||||
Color.Yellow,
|
||||
Color.Pink + 0x80,
|
||||
Color.Pink,
|
||||
Color.White,
|
||||
Color.White
|
||||
],
|
||||
|
||||
// Sharp:A2_ColorSet_DARK_GREEN
|
||||
[
|
||||
Color.Black,
|
||||
Color.Black,
|
||||
Color.Brown + 0x80,
|
||||
Color.Orange + 0x80,
|
||||
Color.DarkGreen,
|
||||
Color.DarkGray,
|
||||
Color.Green,
|
||||
Color.Yellow + 0x80,
|
||||
Color.DarkBlue + 0x80,
|
||||
Color.Purple,
|
||||
Color.LightGray,
|
||||
Color.Pink,
|
||||
Color.MediumBlue,
|
||||
Color.LightBlue,
|
||||
Color.Aqua,
|
||||
Color.White,
|
||||
Color.Black,
|
||||
Color.Red + 0x80,
|
||||
Color.Orange,
|
||||
Color.Orange,
|
||||
Color.DarkGray,
|
||||
Color.DarkGray,
|
||||
Color.Yellow,
|
||||
Color.Yellow,
|
||||
Color.Purple + 0x80,
|
||||
Color.Purple,
|
||||
Color.Pink,
|
||||
Color.Pink,
|
||||
Color.LightBlue + 0x80,
|
||||
Color.LightBlue,
|
||||
Color.White,
|
||||
Color.White
|
||||
],
|
||||
|
||||
// Sharp:A2_ColorSet_BROWN
|
||||
[
|
||||
Color.Black,
|
||||
Color.Black,
|
||||
Color.Red + 0x80,
|
||||
Color.Purple + 0x80,
|
||||
Color.Brown,
|
||||
Color.LightGray,
|
||||
Color.Orange,
|
||||
Color.Pink + 0x80,
|
||||
Color.DarkGreen + 0x80,
|
||||
Color.MediumBlue,
|
||||
Color.DarkGray,
|
||||
Color.LightBlue,
|
||||
Color.Green,
|
||||
Color.Aqua,
|
||||
Color.Yellow,
|
||||
Color.White,
|
||||
Color.Black,
|
||||
Color.DarkBlue + 0x80,
|
||||
Color.Purple,
|
||||
Color.Purple,
|
||||
Color.LightGray,
|
||||
Color.LightGray,
|
||||
Color.Pink,
|
||||
Color.Pink,
|
||||
Color.MediumBlue + 0x80,
|
||||
Color.MediumBlue,
|
||||
Color.LightBlue,
|
||||
Color.LightBlue,
|
||||
Color.Aqua + 0x80,
|
||||
Color.Aqua,
|
||||
Color.White,
|
||||
Color.White
|
||||
],
|
||||
|
||||
// Sharp:A2_ColorSet_RED
|
||||
[
|
||||
Color.Black,
|
||||
Color.Black,
|
||||
Color.DarkBlue + 0x80,
|
||||
Color.MediumBlue + 0x80,
|
||||
Color.Red,
|
||||
Color.DarkGray,
|
||||
Color.Purple,
|
||||
Color.LightBlue + 0x80,
|
||||
Color.Brown + 0x80,
|
||||
Color.Green,
|
||||
Color.LightGray,
|
||||
Color.Aqua,
|
||||
Color.Orange,
|
||||
Color.Yellow,
|
||||
Color.Pink,
|
||||
Color.White,
|
||||
Color.Black,
|
||||
Color.DarkGreen + 0x80,
|
||||
Color.MediumBlue,
|
||||
Color.MediumBlue,
|
||||
Color.DarkGray,
|
||||
Color.DarkGray,
|
||||
Color.LightBlue,
|
||||
Color.LightBlue,
|
||||
Color.Green + 0x80,
|
||||
Color.Green,
|
||||
Color.Aqua,
|
||||
Color.Aqua,
|
||||
Color.Yellow + 0x80,
|
||||
Color.Yellow,
|
||||
Color.White,
|
||||
Color.White
|
||||
]
|
||||
]
|
||||
];
|
37
src/data/laz_sharp
Normal file
37
src/data/laz_sharp
Normal file
@ -0,0 +1,37 @@
|
||||
[
|
||||
// prevBit 0
|
||||
[
|
||||
// nextBit 0
|
||||
[
|
||||
-1, -1, 0, 0, -1, -1, -1, 99,
|
||||
0, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, 99, -1, -1, -1, -1, -1, -1,
|
||||
0, -1, -1, -1, 99, -1, -1, -1
|
||||
],
|
||||
// nextBit 1
|
||||
[
|
||||
-1, -1, 0, 0, -1, -1, -1, 15,
|
||||
99, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, 99, -1, -1, -1, -1, -1, -1,
|
||||
99, -1, -1, -1, 99, -1, -1, -1
|
||||
]
|
||||
],
|
||||
|
||||
// prevBit 1
|
||||
[
|
||||
//nextBit 0
|
||||
[
|
||||
-1, -1, 99, 99, -1, -1, -1, 99,
|
||||
0, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, 99, -1, -1, -1, -1, -1, -1,
|
||||
0, -1, -1, -1, 15, -1, -1, -1
|
||||
],
|
||||
// nextBit 1
|
||||
[
|
||||
-1, -1, 99, 99, -1, -1, -1, 15,
|
||||
99, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, 0, -1, -1, -1, -1, -1, -1,
|
||||
0, -1, -1, -1, 15, -1, -1, -1
|
||||
]
|
||||
]
|
||||
];
|
130
src/data/lores_test
Normal file
130
src/data/lores_test
Normal file
@ -0,0 +1,130 @@
|
||||
[
|
||||
0x00, 0x00, 0x55, 0x05, 0x05, 0x15, 0x15, 0x25,
|
||||
0x25, 0x35, 0x35, 0x45, 0x45, 0x55, 0x55, 0x65,
|
||||
0x65, 0x75, 0x75, 0x85, 0x85, 0x95, 0x95, 0xa5,
|
||||
0xa5, 0xb5, 0xb5, 0xc5, 0xc5, 0xd5, 0xd5, 0xe5,
|
||||
0xe5, 0xf5, 0xf5, 0x55, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x55, 0x00, 0x00, 0x11, 0x11, 0x22,
|
||||
0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66,
|
||||
0x66, 0x77, 0x77, 0x88, 0x88, 0x99, 0x99, 0xaa,
|
||||
0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0xdd, 0xdd, 0xee,
|
||||
0xee, 0xff, 0xff, 0x55, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x55, 0x00, 0x00, 0x11, 0x11, 0x22,
|
||||
0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66,
|
||||
0x66, 0x77, 0x77, 0x88, 0x88, 0x99, 0x99, 0xaa,
|
||||
0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0xdd, 0xdd, 0xee,
|
||||
0xee, 0xff, 0xff, 0x55, 0x00, 0x00, 0x00, 0x00,
|
||||
0x1c, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x38, 0x00,
|
||||
0x00, 0x00, 0x55, 0x00, 0x00, 0x11, 0x11, 0x22,
|
||||
0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66,
|
||||
0x66, 0x77, 0x77, 0x88, 0x88, 0x99, 0x99, 0xaa,
|
||||
0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0xdd, 0xdd, 0xee,
|
||||
0xee, 0xff, 0xff, 0x55, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x55, 0x00, 0x00, 0x11, 0x11, 0x22,
|
||||
0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66,
|
||||
0x66, 0x77, 0x77, 0x88, 0x88, 0x99, 0x99, 0xaa,
|
||||
0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0xdd, 0xdd, 0xee,
|
||||
0xee, 0xff, 0xff, 0x55, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x55, 0x00, 0x00, 0x11, 0x11, 0x22,
|
||||
0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66,
|
||||
0x66, 0x77, 0x77, 0x88, 0x88, 0x99, 0x99, 0xaa,
|
||||
0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0xdd, 0xdd, 0xee,
|
||||
0xee, 0xff, 0xff, 0x55, 0x00, 0x00, 0x00, 0x00,
|
||||
0x04, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x55, 0x00, 0x00, 0x11, 0x11, 0x22,
|
||||
0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66,
|
||||
0x66, 0x77, 0x77, 0x88, 0x88, 0x99, 0x99, 0xaa,
|
||||
0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0xdd, 0xdd, 0xee,
|
||||
0xee, 0xff, 0xff, 0x55, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x55, 0x00, 0x00, 0x11, 0x11, 0x22,
|
||||
0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66,
|
||||
0x66, 0x77, 0x77, 0x88, 0x88, 0x99, 0x99, 0xaa,
|
||||
0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0xdd, 0xdd, 0xee,
|
||||
0xee, 0xff, 0xff, 0x55, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x55, 0x00, 0x00, 0x11, 0x11, 0x22,
|
||||
0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66,
|
||||
0x66, 0x77, 0x77, 0x88, 0x88, 0x99, 0x99, 0xaa,
|
||||
0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0xdd, 0xdd, 0xee,
|
||||
0xee, 0xff, 0xff, 0x55, 0x00, 0x00, 0x00, 0x00,
|
||||
0x2e, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x55, 0x00, 0x00, 0x11, 0x11, 0x22,
|
||||
0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66,
|
||||
0x66, 0x77, 0x77, 0x88, 0x88, 0x99, 0x99, 0xaa,
|
||||
0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0xdd, 0xdd, 0xee,
|
||||
0xee, 0xff, 0xff, 0x55, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x55, 0x00, 0x00, 0x11, 0x11, 0x22,
|
||||
0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66,
|
||||
0x66, 0x77, 0x77, 0x88, 0x88, 0x99, 0x99, 0xaa,
|
||||
0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0xdd, 0xdd, 0xee,
|
||||
0xee, 0xff, 0xff, 0x55, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x55, 0x50, 0x50, 0x51, 0x51, 0x52,
|
||||
0x52, 0x53, 0x53, 0x54, 0x54, 0x55, 0x55, 0x56,
|
||||
0x56, 0x57, 0x57, 0x58, 0x58, 0x59, 0x59, 0x5a,
|
||||
0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5d, 0x5d, 0x5e,
|
||||
0x5e, 0x5f, 0x5f, 0x55, 0x00, 0x00, 0x00, 0x00,
|
||||
0x60, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x55, 0x00, 0x00, 0x11, 0x11, 0x22,
|
||||
0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66,
|
||||
0x66, 0x77, 0x77, 0x88, 0x88, 0x99, 0x99, 0xaa,
|
||||
0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0xdd, 0xdd, 0xee,
|
||||
0xee, 0xff, 0xff, 0x55, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x55, 0x00, 0x00, 0x11, 0x11, 0x22,
|
||||
0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66,
|
||||
0x66, 0x77, 0x77, 0x88, 0x88, 0x99, 0x99, 0xaa,
|
||||
0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0xdd, 0xdd, 0xee,
|
||||
0xee, 0xff, 0xff, 0x55, 0x00, 0x00, 0x00, 0x00,
|
||||
0xa0, 0xa0, 0xa0, 0xc2, 0xa0, 0xcd, 0xa0, 0xc4,
|
||||
0xa0, 0xd0, 0xa0, 0xc4, 0xa0, 0xc7, 0xa0, 0xcd,
|
||||
0xa0, 0xcc, 0xa0, 0xc2, 0xa0, 0xcf, 0xa0, 0xc7,
|
||||
0xa0, 0xd0, 0xa0, 0xcc, 0xa0, 0xd9, 0xa0, 0xc1,
|
||||
0xa0, 0xd7, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x55, 0x00, 0x00, 0x11, 0x11, 0x22,
|
||||
0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66,
|
||||
0x66, 0x77, 0x77, 0x88, 0x88, 0x99, 0x99, 0xaa,
|
||||
0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0xdd, 0xdd, 0xee,
|
||||
0xee, 0xff, 0xff, 0x55, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x55, 0x00, 0x00, 0x11, 0x11, 0x22,
|
||||
0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66,
|
||||
0x66, 0x77, 0x77, 0x88, 0x88, 0x99, 0x99, 0xaa,
|
||||
0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0xdd, 0xdd, 0xee,
|
||||
0xee, 0xff, 0xff, 0x55, 0x00, 0x00, 0x00, 0x00,
|
||||
0xa0, 0xa0, 0xa0, 0xcc, 0xa0, 0xc7, 0xa0, 0xc2,
|
||||
0xa0, 0xd5, 0xa0, 0xc7, 0xa0, 0xd2, 0xa0, 0xc2,
|
||||
0xa0, 0xc2, 0xa0, 0xd2, 0xa0, 0xd2, 0xa0, 0xd2,
|
||||
0xa0, 0xc9, 0xa0, 0xc7, 0xa0, 0xc5, 0xa0, 0xd1,
|
||||
0xa0, 0xc9, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
|
||||
0x02, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00,
|
||||
0x00, 0x00, 0x55, 0x00, 0x00, 0x11, 0x11, 0x22,
|
||||
0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66,
|
||||
0x66, 0x77, 0x77, 0x88, 0x88, 0x99, 0x99, 0xaa,
|
||||
0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0xdd, 0xdd, 0xee,
|
||||
0xee, 0xff, 0xff, 0x55, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x55, 0x00, 0x00, 0x11, 0x11, 0x22,
|
||||
0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66,
|
||||
0x66, 0x77, 0x77, 0x88, 0x88, 0x99, 0x99, 0xaa,
|
||||
0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0xdd, 0xdd, 0xee,
|
||||
0xee, 0xff, 0xff, 0x55, 0x00, 0x00, 0x00, 0x00,
|
||||
0xa0, 0xa0, 0xa0, 0xc1, 0xa0, 0xd4, 0xa0, 0xcc,
|
||||
0xa0, 0xd2, 0xa0, 0xd2, 0xa0, 0xc5, 0xa0, 0xcc,
|
||||
0xa0, 0xcc, 0xa0, 0xd7, 0xa0, 0xce, 0xa0, 0xc5,
|
||||
0xa0, 0xce, 0xa0, 0xd2, 0xa0, 0xcc, 0xa0, 0xd5,
|
||||
0xa0, 0xd4, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x55, 0x00, 0x00, 0x11, 0x11, 0x22,
|
||||
0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66,
|
||||
0x66, 0x77, 0x77, 0x88, 0x88, 0x99, 0x99, 0xaa,
|
||||
0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0xdd, 0xdd, 0xee,
|
||||
0xee, 0xff, 0xff, 0x55, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x55, 0x00, 0x00, 0x11, 0x11, 0x22,
|
||||
0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66,
|
||||
0x66, 0x77, 0x77, 0x88, 0x88, 0x99, 0x99, 0xaa,
|
||||
0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0xdd, 0xdd, 0xee,
|
||||
0xee, 0xff, 0xff, 0x55, 0x00, 0x00, 0x00, 0x00,
|
||||
0xa0, 0xa0, 0xa0, 0xcb, 0xa0, 0xc1, 0xa0, 0xd5,
|
||||
0xa0, 0xd0, 0xa0, 0xce, 0xa0, 0xd9, 0xa0, 0xd5,
|
||||
0xa0, 0xd5, 0xa0, 0xce, 0xa0, 0xc7, 0xa0, 0xd9,
|
||||
0xa0, 0xcb, 0xa0, 0xce, 0xa0, 0xcf, 0xa0, 0xc1,
|
||||
0xa0, 0xc5, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x00
|
||||
];
|
65
src/data/palette_5_6_5
Normal file
65
src/data/palette_5_6_5
Normal file
@ -0,0 +1,65 @@
|
||||
[
|
||||
// Ellsworth
|
||||
[
|
||||
0x0000, 0x7908, 0x3A60, 0xBB68, 0x02E7, 0x7BEF, 0x3D47, 0xBE4F,
|
||||
0x418F, 0xBA97, 0x7BEF, 0xFCF7, 0x4477, 0xBD7F, 0x7ED7, 0xFFDF,
|
||||
0x0000, 0x3864, 0x1920, 0x59A4, 0x0163, 0x39E7, 0x1AA3, 0x6BA5,
|
||||
0x18A7, 0x612C, 0x39E7, 0xC808, 0x222B, 0x201A, 0x242C, 0x7BEF
|
||||
],
|
||||
|
||||
// DeBabelizer
|
||||
[
|
||||
0x0000, 0xF800, 0x7A40, 0xFAA0, 0x0320, 0x4A69, 0x07E0, 0xFFE0,
|
||||
0x000E, 0xF81A, 0xA534, 0xFBEC, 0x001C, 0x3CFF, 0x37F4, 0xFFFF,
|
||||
0x0000, 0x7800, 0x3900, 0x7940, 0x0180, 0x2124, 0x03E0, 0x7BE0,
|
||||
0x0007, 0x780D, 0x528A, 0xB0E0, 0x000E, 0x0273, 0x04CA, 0x7BEF
|
||||
],
|
||||
|
||||
// Laz's TV
|
||||
[
|
||||
0x0000, 0xB807, 0x7BE0, 0xFBE0, 0x3BEF, 0x7BEF, 0x3FE0, 0xBFE7,
|
||||
0x381F, 0xF9FF, 0x7BEF, 0xFBEF, 0x3BFF, 0xBBFF, 0x7DF7, 0xFFFF,
|
||||
0x0000, 0x5844, 0x3A01, 0x81E1, 0x1A08, 0x4208, 0x1C01, 0x5C03,
|
||||
0x184F, 0x80F0, 0x4208, 0x8208, 0x1A10, 0x5A0F, 0x3AEB, 0x8410
|
||||
],
|
||||
|
||||
// Bernie
|
||||
[
|
||||
0x0000, 0xC806, 0x9B00, 0xFB00, 0x0306, 0x528A, 0x06E0, 0xFFE0,
|
||||
0x0013, 0xC999, 0xAD75, 0xFCF3, 0x319F, 0x64FF, 0x37F3, 0xFFFF,
|
||||
0x0000, 0x5801, 0x4120, 0x7900, 0x0142, 0x18E3, 0x0320, 0x63A0,
|
||||
0x1008, 0x600B, 0x4228, 0x71C7, 0x200E, 0x29AE, 0x03A6, 0x6B6D
|
||||
],
|
||||
|
||||
// Gus
|
||||
[
|
||||
0x0000, 0xE861, 0x6302, 0xFB01, 0x1562, 0x528A, 0x17C2, 0xFFC3,
|
||||
0x1051, 0xF89F, 0xCE79, 0xFE7F, 0x107F, 0x667F, 0x3673, 0xFFFF,
|
||||
0x0000, 0x7821, 0x3181, 0x7981, 0x12A0, 0x2945, 0x1401, 0x7BE2,
|
||||
0x0828, 0x804F, 0x632C, 0x7B2F, 0x0850, 0x2B30, 0x1B29, 0x8410
|
||||
],
|
||||
|
||||
// Canon DV
|
||||
[
|
||||
0x0000, 0x8040, 0x1261, 0xA221, 0x0A68, 0x4A69, 0x1562, 0x6DC1,
|
||||
0x287A, 0xC07D, 0x4A69, 0xF26B, 0x129F, 0x929F, 0x15D4, 0xFFFF,
|
||||
0x0000, 0x4020, 0x0940, 0x5121, 0x0924, 0x2124, 0x12A0, 0x32C1,
|
||||
0x184D, 0x606E, 0x2124, 0x7925, 0x094F, 0x4950, 0x0AEA, 0x8410
|
||||
],
|
||||
|
||||
// IREZ CapSure
|
||||
[
|
||||
0x0000, 0xA105, 0x2B62, 0xDC21, 0x138D, 0x8430, 0x1F02, 0xB7A1,
|
||||
0x58FE, 0xF93F, 0x8C51, 0xFCB4, 0x2C3F, 0xE4BF, 0x57DD, 0xFFFF,
|
||||
0x0000, 0x5082, 0x11A1, 0x6A21, 0x11C7, 0x4208, 0x0B82, 0x53C1,
|
||||
0x288F, 0x80AF, 0x4228, 0x7A4A, 0x1A30, 0x7270, 0x2BEF, 0x8410
|
||||
],
|
||||
|
||||
// IIGS
|
||||
[
|
||||
0x0000, 0xD806, 0x8AA0, 0xFB20, 0x03A4, 0x52AA, 0x16E0, 0xFFE0,
|
||||
0x0013, 0xD91B, 0xAD55, 0xFCD1, 0x211F, 0x655F, 0x47F3, 0xFFFF,
|
||||
0x0000, 0x6843, 0x4141, 0x8181, 0x01E2, 0x2945, 0x0B62, 0x7BE2,
|
||||
0x0009, 0x688D, 0x52AA, 0x7A68, 0x1090, 0x2AB0, 0x1C09, 0x8410
|
||||
]
|
||||
];
|
111
src/device/annunciator.d
Normal file
111
src/device/annunciator.d
Normal file
@ -0,0 +1,111 @@
|
||||
/+
|
||||
+ device/annunciator.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 device.annunciator;
|
||||
|
||||
import device.base;
|
||||
import memory;
|
||||
|
||||
class Annunciator
|
||||
{
|
||||
bool[4] ann;
|
||||
|
||||
void reboot()
|
||||
{
|
||||
ann[0..4] = false;
|
||||
}
|
||||
|
||||
void activate(int index)
|
||||
{
|
||||
if (!ann[index])
|
||||
{
|
||||
ann[index] = true;
|
||||
}
|
||||
}
|
||||
|
||||
void deactivate(int index)
|
||||
{
|
||||
if (ann[index])
|
||||
{
|
||||
ann[index] = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ann_0_On()
|
||||
{
|
||||
activate(0);
|
||||
}
|
||||
|
||||
void ann_1_On()
|
||||
{
|
||||
activate(1);
|
||||
}
|
||||
|
||||
void ann_2_On()
|
||||
{
|
||||
activate(2);
|
||||
}
|
||||
|
||||
void ann_3_On()
|
||||
{
|
||||
activate(3);
|
||||
}
|
||||
|
||||
void ann_0_Off()
|
||||
{
|
||||
deactivate(0);
|
||||
}
|
||||
|
||||
void ann_1_Off()
|
||||
{
|
||||
deactivate(1);
|
||||
}
|
||||
|
||||
void ann_2_Off()
|
||||
{
|
||||
deactivate(2);
|
||||
}
|
||||
|
||||
void ann_3_Off()
|
||||
{
|
||||
deactivate(3);
|
||||
}
|
||||
|
||||
mixin(EmptyInitSwitches());
|
||||
}
|
||||
|
||||
class Annunciator_II : Annunciator
|
||||
{
|
||||
mixin(InitSwitches("super", [
|
||||
mixin(MakeSwitch([0xC058], "R0W", "ann_0_Off")),
|
||||
mixin(MakeSwitch([0xC059], "R0W", "ann_0_On")),
|
||||
mixin(MakeSwitch([0xC05A], "R0W", "ann_1_Off")),
|
||||
mixin(MakeSwitch([0xC05B], "R0W", "ann_1_On")),
|
||||
mixin(MakeSwitch([0xC05C], "R0W", "ann_2_Off")),
|
||||
mixin(MakeSwitch([0xC05D], "R0W", "ann_2_On")),
|
||||
mixin(MakeSwitch([0xC05E], "R0W", "ann_3_Off")),
|
||||
mixin(MakeSwitch([0xC05F], "R0W", "ann_3_On"))
|
||||
]));
|
||||
}
|
||||
|
||||
// NOTE: IIe uses Annunciator (the switches are handled by the IOU)
|
||||
// NOTE: IIc has no annunciators
|
203
src/device/base.d
Normal file
203
src/device/base.d
Normal file
@ -0,0 +1,203 @@
|
||||
/+
|
||||
+ device/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 device.base;
|
||||
|
||||
string hex2Digits(int decimalByte)
|
||||
{
|
||||
int highNibble = (decimalByte & 0xF0) >> 4;
|
||||
int lowNibble = decimalByte & 0x0F;
|
||||
|
||||
string digits = "0123456789ABCDEF";
|
||||
|
||||
return digits[highNibble..(highNibble + 1)] ~
|
||||
digits[lowNibble..(lowNibble + 1)];
|
||||
}
|
||||
|
||||
string hex4Digits(int decimalWord)
|
||||
{
|
||||
return hex2Digits(decimalWord >> 8) ~
|
||||
hex2Digits(decimalWord & 0xFF);
|
||||
}
|
||||
|
||||
string InitSwitches(string superCall, string[][] switches)
|
||||
{
|
||||
string initFunc =
|
||||
"void initSwitches(SoftSwitchPage switches, " ~
|
||||
"int slot = -1)\n{\n";
|
||||
|
||||
if (superCall == "super")
|
||||
{
|
||||
initFunc ~= "super.initSwitches(switches, slot);\n";
|
||||
}
|
||||
|
||||
initFunc ~=
|
||||
"ubyte offset = (slot == -1) ? 0 : slot * 16;\n";
|
||||
|
||||
|
||||
string switchFuncs;
|
||||
for(int sw = 0; sw < switches.length; ++sw)
|
||||
{
|
||||
initFunc ~= switches[sw][0] ~ "\n";
|
||||
switchFuncs ~= switches[sw][1] ~ "\n";
|
||||
}
|
||||
|
||||
initFunc ~= "}\n";
|
||||
return initFunc ~ "\n" ~ switchFuncs;
|
||||
}
|
||||
|
||||
/+
|
||||
Read:
|
||||
ubyte wrapper(ushort addr) { wrapped(); return 0; }
|
||||
ubyte wrapper(ushort addr) { return wrapped(); }
|
||||
ubyte wrapped(ushort addr)
|
||||
Write:
|
||||
void wrapper(ushort addr, ubyte val) { wrapped(); }
|
||||
void wrapper(ushort addr, ubyte val) { wrapped(val); }
|
||||
void wrapper(ushort addr, ubyte val) { wrapped(addr); }
|
||||
void wrapped(ushort addr, ubyte val)
|
||||
|
||||
Wrapped:
|
||||
same called for r/w:
|
||||
void func() void func(addr)
|
||||
void func(rw) void func(rw, addr) void func(rw, addr, val)
|
||||
ubyte func()
|
||||
ubyte func(rw) ubyte func(rw, addr) ubyte func(rw, addr, val)
|
||||
only read:
|
||||
void func() void func(addr) ubyte(func) <passthru>
|
||||
only write:
|
||||
void func() void func(addr) <passthru>
|
||||
RETURN VAL: (only applies to read) "0" = void, " "/"7" = ubyte
|
||||
WRAP STYLE:
|
||||
"R? " (read only) could be: ()/(addr) (+ rw)
|
||||
" W?" (write only) could be: ()/(addr)/(val)/(addr, val) (+ rw)
|
||||
"R?W?" (both) could be: ()/(addr) (+ rw)
|
||||
OR: (rw, val)/(rw, addr, val)
|
||||
+/
|
||||
|
||||
string MakeSwitch(int[] addrs, string type, string wrapped)
|
||||
{
|
||||
string initCalls = "";
|
||||
string funcBody;
|
||||
|
||||
string readType, writeType;
|
||||
if (type[0] == 'R')
|
||||
{
|
||||
if (type.length == 1) readType = "R";
|
||||
else if (type[1] == 'W')
|
||||
{
|
||||
readType = "R";
|
||||
writeType = "W";
|
||||
}
|
||||
else
|
||||
{
|
||||
readType = type[0..2];
|
||||
if (type.length > 2) writeType = "W";
|
||||
}
|
||||
}
|
||||
else writeType = "W";
|
||||
|
||||
string wrapper, realWrapped;
|
||||
string rSwitch, wSwitch, args;
|
||||
|
||||
realWrapped = wrapped;
|
||||
if (wrapped[length - 1] == ')')
|
||||
{
|
||||
for (int pos = 0; pos < wrapped.length; ++pos)
|
||||
{
|
||||
if (wrapped[pos] == '(')
|
||||
{
|
||||
args = wrapped[(pos + 1) .. (length - 1)];
|
||||
realWrapped = wrapped[0 .. pos];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
wrapper = "wrapper_" ~ realWrapped;
|
||||
|
||||
if (readType == "R")
|
||||
{
|
||||
rSwitch = "setRSwitch";
|
||||
funcBody = MakeRSwitch(wrapper, realWrapped, args);
|
||||
}
|
||||
else if (readType == "R7")
|
||||
{
|
||||
rSwitch = "setR7Switch";
|
||||
funcBody = MakeRSwitch(wrapper, realWrapped, args);
|
||||
}
|
||||
else if (readType == "R0")
|
||||
{
|
||||
rSwitch = "setR0Switch";
|
||||
funcBody = MakeR0Switch(wrapper, realWrapped, args);
|
||||
}
|
||||
|
||||
if (writeType == "W")
|
||||
{
|
||||
wSwitch = "setWSwitch";
|
||||
funcBody ~= MakeWSwitch(wrapper, realWrapped, args);
|
||||
}
|
||||
|
||||
for (int ad = 0; ad < addrs.length; ++ad)
|
||||
{
|
||||
string addrStr = "(0x" ~ hex4Digits(addrs[ad]) ~ " + offset)";
|
||||
if (rSwitch != "")
|
||||
initCalls ~= "switches." ~ rSwitch ~ "(" ~ addrStr ~ ", &" ~
|
||||
wrapper ~ ");\n";
|
||||
if (wSwitch != "")
|
||||
initCalls ~= "switches." ~ wSwitch ~ "(" ~ addrStr ~ ", &" ~
|
||||
wrapper ~ ");\n";
|
||||
}
|
||||
|
||||
return "[\"" ~ initCalls ~ "\", \"" ~ funcBody ~ "\"]";
|
||||
}
|
||||
|
||||
string MakeR0Switch(string wrapper, string wrapped, string args)
|
||||
{
|
||||
return "ubyte " ~ wrapper ~ "(ushort addr)\n" ~
|
||||
"{\n" ~ wrapped ~ "(" ~ args ~ ");\n" ~
|
||||
"return 0;\n}\n";
|
||||
}
|
||||
|
||||
string MakeRSwitch(string wrapper, string wrapped, string args)
|
||||
{
|
||||
return "ubyte " ~ wrapper ~ "(ushort addr)\n" ~
|
||||
"{\n" ~ "return " ~ wrapped ~ "(" ~ args ~ ");\n}\n";
|
||||
}
|
||||
|
||||
string MakeWSwitch(string wrapper, string wrapped, string args)
|
||||
{
|
||||
return "void " ~ wrapper ~ "(ushort addr, ubyte val)\n" ~
|
||||
"{\n" ~ wrapped ~ "(" ~ args ~ ");\n}\n";
|
||||
}
|
||||
|
||||
string AbstractInitSwitches()
|
||||
{
|
||||
return
|
||||
"abstract void initSwitches(SoftSwitchPage switches, " ~
|
||||
"int slot = -1);\n";
|
||||
}
|
||||
|
||||
string EmptyInitSwitches()
|
||||
{
|
||||
return
|
||||
"void initSwitches(SoftSwitchPage switches, int slot = -1) {}\n";
|
||||
}
|
412
src/device/keyboard.d
Normal file
412
src/device/keyboard.d
Normal file
@ -0,0 +1,412 @@
|
||||
/+
|
||||
+ device/keyboard.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 device.keyboard;
|
||||
|
||||
import device.base;
|
||||
import memory;
|
||||
import device.pushbutton;
|
||||
|
||||
const int KEY_BKSP = 65288; // Backspace (DELETE)
|
||||
const int KEY_TAB = 65289; // Tab
|
||||
const int KEY_RETURN = 65293; // Enter
|
||||
const int KEY_ESCAPE = 65307; // ESC
|
||||
const int KEY_LEFT = 65361; // left arrow
|
||||
const int KEY_UP = 65362; // up arrow
|
||||
const int KEY_RIGHT = 65363; // right arrow
|
||||
const int KEY_DOWN = 65364; // down arrow
|
||||
const int KEY_LSHIFT = 65505; // left shift
|
||||
const int KEY_RSHIFT = 65506; // right sight
|
||||
const int KEY_LOS = 65515; // left "windows" (open apple)
|
||||
const int KEY_ROS = 65516; // right "windows" (closed apple)
|
||||
|
||||
const int KEY_LOWER_MIN = 97;
|
||||
const int KEY_LOWER_MAX = 122;
|
||||
const int KEY_UPPER_MIN = 65;
|
||||
const int KEY_UPPER_MAX = 90;
|
||||
|
||||
// !"#$%&'()*+,-./0123456789:;<=>?@
|
||||
const int KEY_SYMBOL_MIN = 32;
|
||||
const int KEY_SYMBOL_MAX = 64;
|
||||
const int KEY_SYMBOL2_MAX = 126;
|
||||
|
||||
const int KEY_BRACKETRIGHT = 93;
|
||||
const int KEY_CIRCUM = 94;
|
||||
|
||||
import std.stdio;
|
||||
|
||||
class Keyboard
|
||||
{
|
||||
bool keyStrobe;
|
||||
bool anyKeyDown;
|
||||
ubyte latch;
|
||||
bool[int] keysDown;
|
||||
Pushbuttons buttons;
|
||||
|
||||
class RingBuffer
|
||||
{
|
||||
int[] values;
|
||||
int nextRead, nextWrite;
|
||||
|
||||
this(int len)
|
||||
{
|
||||
values.length = len;
|
||||
clear();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
nextRead = nextWrite = 0;
|
||||
}
|
||||
|
||||
bool canRead()
|
||||
{
|
||||
return (nextRead != nextWrite);
|
||||
}
|
||||
|
||||
int read()
|
||||
{
|
||||
// assert(canRead()); XXX
|
||||
int val = values[nextRead];
|
||||
nextRead = (nextRead + 1) % values.length;
|
||||
return val;
|
||||
}
|
||||
|
||||
void write(int val)
|
||||
{
|
||||
int next = (nextWrite + 1) % values.length;
|
||||
if (next != nextRead)
|
||||
{
|
||||
values[nextWrite] = val;
|
||||
nextWrite = next;
|
||||
}
|
||||
else
|
||||
{
|
||||
// XXX Need to clean up press buffer
|
||||
// else emulator misbehaves
|
||||
writefln("Press buffer full");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RingBuffer presses, releases;
|
||||
|
||||
this()
|
||||
{
|
||||
// XXX constants
|
||||
presses = new RingBuffer(20);
|
||||
releases = new RingBuffer(10);
|
||||
}
|
||||
|
||||
void reboot()
|
||||
{
|
||||
latch = 0;
|
||||
keyStrobe = anyKeyDown = false;
|
||||
keysDown = keysDown.init;
|
||||
presses.clear();
|
||||
releases.clear();
|
||||
}
|
||||
|
||||
abstract int appleASCII(int keyval, bool ctrl);
|
||||
abstract bool handleSpecialKey(int keyval, bool keyDown);
|
||||
|
||||
bool handlePress(int keyval, bool ctrl, int keycode)
|
||||
{
|
||||
int ascii = appleASCII(keyval, ctrl);
|
||||
if (ascii < 0)
|
||||
{
|
||||
if (handleSpecialKey(keyval, true)) return true;
|
||||
return false;
|
||||
}
|
||||
recordKeyPress(ascii, keycode);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool handleRelease(int keyval, bool ctrl, int keycode)
|
||||
{
|
||||
int ascii = appleASCII(keyval, ctrl);
|
||||
if (ascii < 0)
|
||||
{
|
||||
if (handleSpecialKey(keyval, false)) return true;
|
||||
return false;
|
||||
}
|
||||
recordKeyRelease(keycode);
|
||||
return true;
|
||||
}
|
||||
|
||||
void processPresses()
|
||||
{
|
||||
if (!presses.canRead()) return;
|
||||
|
||||
anyKeyDown = true;
|
||||
keyStrobe = true;
|
||||
|
||||
// assert latch < 0x80; XXX
|
||||
latch = presses.read();
|
||||
keysDown[presses.read()] = true;
|
||||
}
|
||||
|
||||
void processReleases()
|
||||
{
|
||||
if (!releases.canRead()) return;
|
||||
|
||||
int code = releases.read();
|
||||
if (code in keysDown) {
|
||||
keysDown.remove(code);
|
||||
if (keysDown.length == 0)
|
||||
{
|
||||
anyKeyDown = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void recordKeyPress(int ascii, int code)
|
||||
{
|
||||
presses.write(ascii);
|
||||
presses.write(code);
|
||||
}
|
||||
|
||||
void recordKeyRelease(int code)
|
||||
{
|
||||
releases.write(code);
|
||||
}
|
||||
|
||||
ubyte delegate() onReadLatch;
|
||||
void delegate() onClearStrobe;
|
||||
|
||||
ubyte readLatch()
|
||||
{
|
||||
if (onReadLatch !is null)
|
||||
{
|
||||
keyStrobe = true;
|
||||
return onReadLatch() | 0x80;
|
||||
}
|
||||
|
||||
if (keyStrobe)
|
||||
{
|
||||
return latch | 0x80;
|
||||
}
|
||||
else
|
||||
{
|
||||
return latch;
|
||||
}
|
||||
}
|
||||
|
||||
ubyte peekLatch()
|
||||
{
|
||||
return latch;
|
||||
}
|
||||
|
||||
void clearKeystrobe()
|
||||
{
|
||||
if (keyStrobe)
|
||||
{
|
||||
keyStrobe = false;
|
||||
if (onClearStrobe !is null)
|
||||
onClearStrobe();
|
||||
}
|
||||
}
|
||||
|
||||
mixin(AbstractInitSwitches());
|
||||
}
|
||||
|
||||
class Keyboard_II : Keyboard
|
||||
{
|
||||
int appleASCII(int keyval, bool ctrl)
|
||||
{
|
||||
int ascii = -1;
|
||||
if (keyval > 65000)
|
||||
{
|
||||
switch (keyval)
|
||||
{
|
||||
case KEY_RETURN:
|
||||
ascii = 0x0D;
|
||||
break;
|
||||
case KEY_ESCAPE:
|
||||
ascii = 0x1B;
|
||||
break;
|
||||
case KEY_LEFT:
|
||||
ascii = 0x08;
|
||||
break;
|
||||
case KEY_RIGHT:
|
||||
ascii = 0x15;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (keyval >= KEY_SYMBOL_MIN)
|
||||
{
|
||||
if (keyval < KEY_SYMBOL_MAX)
|
||||
{
|
||||
ascii = keyval;
|
||||
}
|
||||
else if (keyval == KEY_SYMBOL_MAX)
|
||||
{
|
||||
if (ctrl)
|
||||
ascii = 0;
|
||||
else
|
||||
ascii = keyval;
|
||||
}
|
||||
else if (keyval >= KEY_UPPER_MIN)
|
||||
{
|
||||
if (keyval <= KEY_UPPER_MAX)
|
||||
{
|
||||
if (ctrl)
|
||||
ascii = keyval - KEY_UPPER_MIN + 1;
|
||||
else
|
||||
ascii = keyval;
|
||||
}
|
||||
else if (keyval == KEY_BRACKETRIGHT ||
|
||||
keyval == KEY_CIRCUM)
|
||||
{
|
||||
ascii = keyval;
|
||||
}
|
||||
else if (keyval >= KEY_LOWER_MIN)
|
||||
{
|
||||
if (keyval <= KEY_LOWER_MAX)
|
||||
{
|
||||
if (ctrl)
|
||||
ascii = keyval - KEY_LOWER_MIN + 1;
|
||||
// XXX
|
||||
// else if lowercase-mod
|
||||
else
|
||||
ascii = keyval - 32;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ascii;
|
||||
}
|
||||
|
||||
bool handleSpecialKey(int keyval, bool keyDown)
|
||||
{
|
||||
// XXX check for shift key mod
|
||||
return false;
|
||||
}
|
||||
|
||||
mixin(InitSwitches("", [
|
||||
mixin(MakeSwitch(
|
||||
[0xC000, 0xC001, 0xC002, 0xC003, 0xC004, 0xC005, 0xC006, 0xC007,
|
||||
0xC008, 0xC009, 0xC00A, 0xC00B, 0xC00C, 0xC00D, 0xC00E, 0xC00F],
|
||||
"R", "readLatch")),
|
||||
mixin(MakeSwitch(
|
||||
[0xC010, 0xC011, 0xC012, 0xC013, 0xC014, 0xC015, 0xC016, 0xC017,
|
||||
0xC018, 0xC019, 0xC01A, 0xC01B, 0xC01C, 0xC01D, 0xC01E, 0xC01F],
|
||||
"R0W", "clearKeystrobe"))
|
||||
]));
|
||||
}
|
||||
|
||||
class Keyboard_IIe : Keyboard
|
||||
{
|
||||
int appleASCII(int keyval, bool ctrl)
|
||||
{
|
||||
int ascii = -1;
|
||||
if (keyval > 65000)
|
||||
{
|
||||
switch (keyval)
|
||||
{
|
||||
case KEY_RETURN:
|
||||
ascii = 0x0D;
|
||||
break;
|
||||
case KEY_ESCAPE:
|
||||
ascii = 0x1B;
|
||||
break;
|
||||
case KEY_LEFT:
|
||||
ascii = 0x08;
|
||||
break;
|
||||
case KEY_RIGHT:
|
||||
ascii = 0x15;
|
||||
break;
|
||||
case KEY_UP:
|
||||
ascii = 0x0B;
|
||||
break;
|
||||
case KEY_DOWN:
|
||||
ascii = 0x0A;
|
||||
break;
|
||||
case KEY_BKSP:
|
||||
ascii = 0x7F;
|
||||
break;
|
||||
case KEY_TAB:
|
||||
ascii = 0x09;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ((keyval >= KEY_SYMBOL_MIN) && (keyval <= KEY_SYMBOL2_MAX))
|
||||
{
|
||||
if (ctrl)
|
||||
{
|
||||
if ((keyval >= KEY_UPPER_MIN) && (keyval <= KEY_UPPER_MAX))
|
||||
ascii = keyval - KEY_UPPER_MIN + 1;
|
||||
else if ((keyval >= KEY_LOWER_MIN) &&
|
||||
(keyval <= KEY_LOWER_MAX))
|
||||
ascii = keyval - KEY_LOWER_MIN + 1;
|
||||
else
|
||||
ascii = keyval;
|
||||
}
|
||||
else
|
||||
{
|
||||
ascii = keyval;
|
||||
}
|
||||
}
|
||||
return ascii;
|
||||
}
|
||||
|
||||
bool handleSpecialKey(int keyval, bool keyDown)
|
||||
{
|
||||
if (keyval == KEY_LOS)
|
||||
{
|
||||
buttons.update(0, keyDown);
|
||||
return true;
|
||||
}
|
||||
else if (keyval == KEY_ROS)
|
||||
{
|
||||
buttons.update(1, keyDown);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ubyte readAKD()
|
||||
{
|
||||
clearKeystrobe();
|
||||
return latch | (anyKeyDown ? 0x80 : 0x00);
|
||||
}
|
||||
|
||||
mixin(InitSwitches("", [
|
||||
mixin(MakeSwitch(
|
||||
[0xC000, 0xC001, 0xC002, 0xC003, 0xC004, 0xC005, 0xC006, 0xC007,
|
||||
0xC008, 0xC009, 0xC00A, 0xC00B, 0xC00C, 0xC00D, 0xC00E, 0xC00F],
|
||||
"R", "readLatch")),
|
||||
mixin(MakeSwitch([0xC010], "R", "readAKD")),
|
||||
mixin(MakeSwitch(
|
||||
[0xC010, 0xC011, 0xC012, 0xC013, 0xC014, 0xC015, 0xC016, 0xC017,
|
||||
0xC018, 0xC019, 0xC01A, 0xC01B, 0xC01C, 0xC01D, 0xC01E, 0xC01F],
|
||||
"W", "clearKeystrobe"))
|
||||
]));
|
||||
}
|
||||
|
161
src/device/paddle.d
Normal file
161
src/device/paddle.d
Normal file
@ -0,0 +1,161 @@
|
||||
/+
|
||||
+ device/paddle.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 device.paddle;
|
||||
|
||||
import device.base;
|
||||
import timer;
|
||||
import memory;
|
||||
|
||||
class Paddles
|
||||
{
|
||||
Timer timer;
|
||||
|
||||
int numPaddles;
|
||||
int[4] onTrigger;
|
||||
bool[4] stillTiming;
|
||||
ubyte[4] switchVal;
|
||||
|
||||
this()
|
||||
{
|
||||
numPaddles = 4;
|
||||
}
|
||||
|
||||
void reboot()
|
||||
{
|
||||
onTrigger[0..4] = 1380; // XXX only if no real joystick ??
|
||||
stillTiming[0..4] = false;
|
||||
switchVal[0..4] = 0;
|
||||
}
|
||||
|
||||
// XXX II: C07X | IIe Orig: C07X | IIe Enh: C070-C07E
|
||||
// (IOU gets C07F)
|
||||
// IIc: as IIe Enh, plus VBLINT?
|
||||
|
||||
mixin(InitSwitches("", [
|
||||
mixin(MakeSwitch(
|
||||
[0xC070, 0xC071, 0xC072, 0xC073, 0xC074, 0xC075, 0xC076, 0xC077,
|
||||
0xC078, 0xC079, 0xC07A, 0xC07B, 0xC07C, 0xC07D, 0xC07E],
|
||||
"R0W", "trigger")),
|
||||
mixin(MakeSwitch([0xC064, 0xC06C], "R7", "check_pdl_0")),
|
||||
mixin(MakeSwitch([0xC065, 0xC06D], "R7", "check_pdl_1")),
|
||||
mixin(MakeSwitch([0xC066, 0xC06E], "R7", "check_pdl_2")),
|
||||
mixin(MakeSwitch([0xC067, 0xC06F], "R7", "check_pdl_3"))
|
||||
]));
|
||||
|
||||
void update(int pdl, int value)
|
||||
{
|
||||
onTrigger[pdl] = value ? value : 1;
|
||||
}
|
||||
|
||||
ubyte check_pdl_0()
|
||||
{
|
||||
return switchVal[0];
|
||||
}
|
||||
|
||||
ubyte check_pdl_1()
|
||||
{
|
||||
return switchVal[1];
|
||||
}
|
||||
ubyte check_pdl_2()
|
||||
{
|
||||
return switchVal[2];
|
||||
}
|
||||
ubyte check_pdl_3()
|
||||
{
|
||||
return switchVal[3];
|
||||
}
|
||||
|
||||
void trigger()
|
||||
{
|
||||
for (int i = 0; i < numPaddles; ++i)
|
||||
{
|
||||
if (stillTiming[i]) return;
|
||||
}
|
||||
for (int i = 0; i < numPaddles; ++i)
|
||||
{
|
||||
if (onTrigger[i] == 0) onTrigger[i] = 1;
|
||||
switchVal[i] = 0x80;
|
||||
}
|
||||
timer.new Counter(onTrigger[0], &pdl_0_expired);
|
||||
timer.new Counter(onTrigger[1], &pdl_1_expired);
|
||||
if (numPaddles > 2)
|
||||
{
|
||||
timer.new Counter(onTrigger[2], &pdl_2_expired);
|
||||
timer.new Counter(onTrigger[3], &pdl_3_expired);
|
||||
}
|
||||
}
|
||||
|
||||
bool pdl_0_expired()
|
||||
{
|
||||
stillTiming[0] = false;
|
||||
switchVal[0] = 0x00;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool pdl_1_expired()
|
||||
{
|
||||
stillTiming[1] = false;
|
||||
switchVal[1] = 0x00;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool pdl_2_expired()
|
||||
{
|
||||
stillTiming[2] = false;
|
||||
switchVal[2] = 0x00;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool pdl_3_expired()
|
||||
{
|
||||
stillTiming[3] = false;
|
||||
switchVal[3] = 0x00;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: IIe Unenhanced uses Paddles_II; IIe enhanced uses Paddles
|
||||
// (because C07F is handled first by IOU)
|
||||
|
||||
class Paddles_II : Paddles
|
||||
{
|
||||
mixin(InitSwitches("super", [
|
||||
mixin(MakeSwitch([0xC07F], "R0W", "trigger"))
|
||||
]));
|
||||
}
|
||||
|
||||
// XXX IIc: 0xC07X resets VBLINT along with triggering timers,
|
||||
// C07E/C07F as IIe enhanced, maybe more? (and there
|
||||
// are only two paddles, so no C066/C067/C06E/C06F)
|
||||
|
||||
class Paddles_IIc : Paddles
|
||||
{
|
||||
// XXX add the switches
|
||||
mixin(EmptyInitSwitches());
|
||||
|
||||
this()
|
||||
{
|
||||
super();
|
||||
numPaddles = 2;
|
||||
}
|
||||
}
|
67
src/device/pushbutton.d
Normal file
67
src/device/pushbutton.d
Normal file
@ -0,0 +1,67 @@
|
||||
/+
|
||||
+ device/pushbutton.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 device.pushbutton;
|
||||
|
||||
import device.base;
|
||||
import memory;
|
||||
|
||||
class Pushbuttons
|
||||
{
|
||||
bool buttons[3];
|
||||
|
||||
void reboot()
|
||||
{
|
||||
buttons[0..3] = false;
|
||||
}
|
||||
|
||||
void update(int btn, bool isDown)
|
||||
{
|
||||
buttons[btn] = isDown;
|
||||
}
|
||||
|
||||
ubyte read(int btn)
|
||||
{
|
||||
return (buttons[btn] ? 0x80 : 0);
|
||||
}
|
||||
|
||||
ubyte readPB0()
|
||||
{
|
||||
return read(0);
|
||||
}
|
||||
|
||||
ubyte readPB1()
|
||||
{
|
||||
return read(1);
|
||||
}
|
||||
|
||||
ubyte readPB2()
|
||||
{
|
||||
return read(2);
|
||||
}
|
||||
|
||||
mixin(InitSwitches("", [
|
||||
mixin(MakeSwitch([0xC061, 0xC069], "R7", "readPB0")),
|
||||
mixin(MakeSwitch([0xC062, 0xC06A], "R7", "readPB1")),
|
||||
mixin(MakeSwitch([0xC063, 0xC06B], "R7", "readPB2"))
|
||||
]));
|
||||
}
|
138
src/device/speaker.d
Normal file
138
src/device/speaker.d
Normal file
@ -0,0 +1,138 @@
|
||||
/+
|
||||
+ device/speaker.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 device.speaker;
|
||||
|
||||
import std.c.string;
|
||||
|
||||
import device.base;
|
||||
import memory;
|
||||
import timer;
|
||||
|
||||
class Speaker
|
||||
{
|
||||
bool muted;
|
||||
Timer.Cycle cycle;
|
||||
|
||||
int sampleTicks;
|
||||
ushort sample;
|
||||
|
||||
uint lastToggleTick;
|
||||
bool toggled;
|
||||
|
||||
short[] mainBuffer;
|
||||
short[] extraBuffer;
|
||||
uint mainIndex, extraIndex;
|
||||
|
||||
this()
|
||||
{
|
||||
sample = 0x8000;
|
||||
mainBuffer.length = 8192;
|
||||
}
|
||||
|
||||
void setTiming(uint hertz, int sampleFreq, Timer.Cycle deviceCycle)
|
||||
{
|
||||
extraBuffer.length = sampleTicks = (hertz / sampleFreq);
|
||||
cycle = deviceCycle;
|
||||
}
|
||||
|
||||
mixin(InitSwitches("", [
|
||||
mixin(MakeSwitch([0xC030], "R0W", "toggleSpeaker"))
|
||||
]));
|
||||
|
||||
uint processExtraBuffer(uint elapsed)
|
||||
{
|
||||
uint newElapsed = elapsed;
|
||||
|
||||
if (extraIndex != 0)
|
||||
{
|
||||
for (; extraIndex < extraBuffer.length; ++extraIndex)
|
||||
{
|
||||
if (newElapsed == 0) break;
|
||||
extraBuffer[extraIndex] = sample;
|
||||
--newElapsed;
|
||||
}
|
||||
if (extraIndex == extraBuffer.length)
|
||||
{
|
||||
extraIndex = 0;
|
||||
long sampleMean = 0;
|
||||
for (uint i = 0; i < extraBuffer.length; ++i)
|
||||
sampleMean += cast(long)extraBuffer[i];
|
||||
sampleMean /= cast(long)extraBuffer.length;
|
||||
|
||||
if (mainIndex < (mainBuffer.length - 1))
|
||||
{
|
||||
mainBuffer[mainIndex++] =
|
||||
(muted ? 0 : cast(short)sampleMean);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newElapsed;
|
||||
}
|
||||
|
||||
void updateExtraBuffer(uint extraTicks)
|
||||
{
|
||||
if (extraTicks == 0) return;
|
||||
|
||||
for (extraIndex = 0; extraIndex < extraTicks; ++extraIndex)
|
||||
{
|
||||
extraBuffer[extraIndex] =
|
||||
(muted ? 0 : sample);
|
||||
}
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
uint elapsedSinceToggle = cycle.currentVal() - lastToggleTick;
|
||||
lastToggleTick = cycle.currentVal();
|
||||
elapsedSinceToggle = processExtraBuffer(elapsedSinceToggle);
|
||||
|
||||
uint samples = elapsedSinceToggle / sampleTicks;
|
||||
uint extraTicks = elapsedSinceToggle % sampleTicks;
|
||||
|
||||
// update main buffer
|
||||
for (; mainIndex < (mainBuffer.length - 1); ++mainIndex)
|
||||
{
|
||||
if (samples == 0) break;
|
||||
mainBuffer[mainIndex] =
|
||||
(muted ? 0 : sample);
|
||||
--samples;
|
||||
}
|
||||
|
||||
updateExtraBuffer(extraTicks);
|
||||
}
|
||||
|
||||
void toggleSpeaker()
|
||||
{
|
||||
toggled = true;
|
||||
update();
|
||||
sample = ~sample;
|
||||
}
|
||||
|
||||
void clearBuffer()
|
||||
{
|
||||
mainIndex = 0;
|
||||
lastToggleTick = 0;
|
||||
toggled = false;
|
||||
}
|
||||
}
|
160
src/host.d
Normal file
160
src/host.d
Normal file
@ -0,0 +1,160 @@
|
||||
/+
|
||||
+ host.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 host;
|
||||
|
||||
import std.c.time;
|
||||
import std.c.linux.linux;
|
||||
import std.stdio;
|
||||
|
||||
import derelict.sdl.sdl;
|
||||
import derelict.util.exception;
|
||||
|
||||
static this()
|
||||
{
|
||||
delay = new Delay();
|
||||
|
||||
try
|
||||
{
|
||||
DerelictSDL.load();
|
||||
writefln("Loaded lib = %s", DerelictSDL.libName);
|
||||
}
|
||||
catch (DerelictException e)
|
||||
{
|
||||
writefln("%s", e.msg);
|
||||
return;
|
||||
}
|
||||
if (SDL_Init(0) == -1)
|
||||
{
|
||||
writefln("%s", std.string.toString(SDL_GetError()));
|
||||
return;
|
||||
}
|
||||
SDL = true;
|
||||
}
|
||||
|
||||
static ~this()
|
||||
{
|
||||
if (SDL) SDL_Quit();
|
||||
}
|
||||
|
||||
bool SDL;
|
||||
|
||||
class Delay
|
||||
{
|
||||
timeval timeNow, timeShould, timeDiff, timeCheck;
|
||||
uint checkCycles;
|
||||
static float speedCorrection = 1000000.0 / 1020478.0;
|
||||
bool delegate() soundCardActive;
|
||||
bool delegate() soundCardHasEnoughData;
|
||||
|
||||
void sleep()
|
||||
{
|
||||
if (soundCardActive())
|
||||
sleepAudio();
|
||||
else
|
||||
sleepNoAudio();
|
||||
}
|
||||
|
||||
void sleepAudio()
|
||||
{
|
||||
while (soundCardHasEnoughData())
|
||||
{
|
||||
usleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
void nap()
|
||||
{
|
||||
usleep(1000);
|
||||
}
|
||||
|
||||
void sleepNoAudio()
|
||||
{
|
||||
if (timeShould.tv_usec >= 990_000)
|
||||
{
|
||||
++timeShould.tv_sec;
|
||||
timeShould.tv_usec -= 1_000_000;
|
||||
}
|
||||
timeShould.tv_usec += 10_000;
|
||||
|
||||
gettimeofday(&timeNow, null);
|
||||
|
||||
// Assume that tv_sec = 0;
|
||||
if (timeCompare(&timeShould, &timeNow, &timeDiff))
|
||||
usleep(timeDiff.tv_usec);
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
gettimeofday(&timeShould, null);
|
||||
resetSpeedCheck();
|
||||
}
|
||||
|
||||
void resetSpeedCheck()
|
||||
{
|
||||
gettimeofday(&timeCheck, null);
|
||||
checkCycles = 0;
|
||||
}
|
||||
|
||||
float checkSpeedPercent(uint cycles)
|
||||
{
|
||||
checkCycles += cycles;
|
||||
if (checkCycles > 1020478) // do calculation at least once per "second"
|
||||
{
|
||||
gettimeofday(&timeNow, null);
|
||||
timeCompare(&timeNow, &timeCheck, &timeDiff);
|
||||
uint elapsed = (timeDiff.tv_sec * 1000000) + timeDiff.tv_usec;
|
||||
if (elapsed >= 1000000)
|
||||
{
|
||||
float percent = cast(float)checkCycles / cast(float)elapsed;
|
||||
resetSpeedCheck();
|
||||
return percent * speedCorrection;
|
||||
}
|
||||
}
|
||||
return -1.0;
|
||||
}
|
||||
}
|
||||
|
||||
bool timeCompare(timeval* later, timeval* earlier, timeval* diff)
|
||||
{
|
||||
if (later.tv_usec < earlier.tv_usec) {
|
||||
int nsec = (earlier.tv_usec - later.tv_usec) / 1000000 + 1;
|
||||
earlier.tv_usec -= 1000000 * nsec;
|
||||
earlier.tv_sec += nsec;
|
||||
}
|
||||
if (later.tv_usec - earlier.tv_usec > 1000000) {
|
||||
int nsec = (later.tv_usec - earlier.tv_usec) / 1000000;
|
||||
earlier.tv_usec += 1000000 * nsec;
|
||||
earlier.tv_sec -= nsec;
|
||||
}
|
||||
|
||||
if (later.tv_sec >= earlier.tv_sec)
|
||||
{
|
||||
diff.tv_sec = later.tv_sec - earlier.tv_sec;
|
||||
diff.tv_usec = later.tv_usec - earlier.tv_usec;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
Delay delay;
|
310
src/iomem.d
Normal file
310
src/iomem.d
Normal file
@ -0,0 +1,310 @@
|
||||
/+
|
||||
+ iomem.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 memory;
|
||||
import timer;
|
||||
|
||||
import system.peripheral;
|
||||
import peripheral.base;
|
||||
import device.base;
|
||||
|
||||
import std.string;
|
||||
|
||||
class IOMem
|
||||
{
|
||||
// NOTE: It is implied that a peripheral slot with
|
||||
// an I/O STROBE ROM' always responds to I/O STROBE'.
|
||||
class IOSelectMem : Rom
|
||||
{
|
||||
int slotNum;
|
||||
|
||||
this(int slotNum_, ubyte[] rom)
|
||||
{
|
||||
super(0xC000 + (slotNum_ * 0x100), 0x0100, rom);
|
||||
slotNum = slotNum_;
|
||||
}
|
||||
|
||||
ubyte read(ushort addr)
|
||||
{
|
||||
activateStrobeMem(slotNum);
|
||||
|
||||
return data[addr - baseAddress];
|
||||
}
|
||||
|
||||
void write(ushort addr, ubyte val)
|
||||
{
|
||||
activateStrobeMem(slotNum);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: It is implied that all peripherals obey the
|
||||
// $CFFF I/O STROBE' protocol.
|
||||
class IOStrobeMem : Rom
|
||||
{
|
||||
int slotNum; // XXX not just num, but a reference to the
|
||||
// rom object for that slot (for debug
|
||||
// display).
|
||||
|
||||
this(int slotNum_, ubyte[] rom)
|
||||
{
|
||||
super(0xC800, 0x0800, rom);
|
||||
slotNum = slotNum_;
|
||||
}
|
||||
|
||||
ubyte read(ushort addr)
|
||||
{
|
||||
if (addr == 0xCFFF) deactivateStrobeMem();
|
||||
|
||||
return data[addr - 0xC800];
|
||||
}
|
||||
|
||||
void write(ushort addr)
|
||||
{
|
||||
if (addr == 0xCFFF) deactivateStrobeMem();
|
||||
}
|
||||
}
|
||||
|
||||
int strobeSlotNum;
|
||||
IOSelectMem[8] selectMem;
|
||||
IOStrobeMem[8] strobeMem;
|
||||
AddressDecoder decoder;
|
||||
|
||||
void initialize(AddressDecoder decoder_, SoftSwitchPage switches,
|
||||
Timer timer, Peripherals peripherals)
|
||||
{
|
||||
decoder = decoder_;
|
||||
Peripheral card;
|
||||
for (int slot = 0; slot <=7; ++slot)
|
||||
{
|
||||
card = peripherals.cards[slot];
|
||||
if (slot > 0)
|
||||
{
|
||||
if ((card is null) || (card.ioSelectROM is null))
|
||||
{
|
||||
installEmptySelect(slot);
|
||||
}
|
||||
else
|
||||
{
|
||||
selectMem[slot] = new IOSelectMem(slot, card.ioSelectROM);
|
||||
selectMem[slot].debugName =
|
||||
"Slot " ~ std.string.toString(slot) ~ " ROM";
|
||||
decoder.install(selectMem[slot]);
|
||||
if (card.ioStrobeROM !is null)
|
||||
{
|
||||
strobeMem[slot] = new IOStrobeMem(slot,
|
||||
card.ioStrobeROM);
|
||||
strobeMem[slot].debugName = selectMem[slot].debugName;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (card !is null) card.plugIn(slot, switches, timer);
|
||||
}
|
||||
}
|
||||
|
||||
void reboot()
|
||||
{
|
||||
deactivateStrobeMem();
|
||||
}
|
||||
|
||||
void installEmptySelect(int slotNum)
|
||||
{
|
||||
// NOTE: using these read/write delegates implies that
|
||||
// a slot without an I/O SELECT' ROM _cannot_ have an
|
||||
// I/O STROBE' ROM.
|
||||
decoder.installNull(0xC000 + (slotNum * 0x100), 0x100);
|
||||
}
|
||||
|
||||
void activateStrobeMem(int slotNum)
|
||||
{
|
||||
// Do nothing if the I/O STROBE' memory for
|
||||
// the given slotNum is already active.
|
||||
if (strobeSlotNum == slotNum) return;
|
||||
|
||||
if (strobeMem[slotNum] is null)
|
||||
{
|
||||
deactivateStrobeMem();
|
||||
}
|
||||
else
|
||||
{
|
||||
decoder.install(strobeMem[slotNum]);
|
||||
strobeSlotNum = slotNum;
|
||||
}
|
||||
}
|
||||
|
||||
void deactivateStrobeMem()
|
||||
{
|
||||
decoder.installNull(0xC800, 0x0800);
|
||||
strobeSlotNum = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class IOMem_IIe : IOMem
|
||||
{
|
||||
class IntC3ROM : Rom
|
||||
{
|
||||
this(ubyte[] rom)
|
||||
{
|
||||
super(0xC300, 0x0100, rom);
|
||||
}
|
||||
|
||||
ubyte read(ushort addr)
|
||||
{
|
||||
activateIntStrobeMem();
|
||||
return data[addr - 0xC300];
|
||||
}
|
||||
|
||||
void write(ushort addr, ubyte val)
|
||||
{
|
||||
activateIntStrobeMem();
|
||||
}
|
||||
}
|
||||
|
||||
bool intC8ROM, slotC3ROM, intCXROM;
|
||||
IOStrobeMem intStrobeMem;
|
||||
IntC3ROM intC3ROM;
|
||||
Rom c100c2ff, c400c7ff;
|
||||
|
||||
ubyte delegate() kbdLatch;
|
||||
|
||||
void reboot()
|
||||
{
|
||||
super.reboot();
|
||||
resetIntCXROM();
|
||||
resetSlotC3ROM();
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
deactivateStrobeMem();
|
||||
resetIntCXROM();
|
||||
resetSlotC3ROM();
|
||||
}
|
||||
|
||||
void setSlotC3ROM()
|
||||
{
|
||||
if (slotC3ROM) return;
|
||||
slotC3ROM = true;
|
||||
|
||||
// $C3XX cannot be configured for slot response if
|
||||
// INTCXROM is set.
|
||||
if (intCXROM) return;
|
||||
|
||||
if (selectMem[3] !is null)
|
||||
{
|
||||
decoder.install(selectMem[3]);
|
||||
}
|
||||
else
|
||||
{
|
||||
installEmptySelect(3);
|
||||
}
|
||||
}
|
||||
|
||||
void resetSlotC3ROM()
|
||||
{
|
||||
slotC3ROM = false;
|
||||
decoder.install(intC3ROM);
|
||||
}
|
||||
|
||||
void setIntCXROM()
|
||||
{
|
||||
if (intCXROM) return;
|
||||
intCXROM = true;
|
||||
decoder.install(c100c2ff);
|
||||
decoder.install(intC3ROM);
|
||||
decoder.install(c400c7ff);
|
||||
decoder.install(intStrobeMem);
|
||||
}
|
||||
|
||||
void resetIntCXROM()
|
||||
{
|
||||
intCXROM = false;
|
||||
for (int s = 1; s <= 7; ++s)
|
||||
{
|
||||
if (selectMem[s] !is null)
|
||||
{
|
||||
if ((s != 3) || (slotC3ROM)) decoder.install(selectMem[s]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((s != 3) || (slotC3ROM)) installEmptySelect(s);
|
||||
}
|
||||
}
|
||||
if (!intC8ROM) deactivateStrobeMem();
|
||||
}
|
||||
|
||||
void activateIntStrobeMem()
|
||||
{
|
||||
if (intC8ROM) return;
|
||||
|
||||
decoder.install(intStrobeMem);
|
||||
strobeSlotNum = -1; // XXX hack (-1 represents internal?)
|
||||
intC8ROM = true;
|
||||
}
|
||||
|
||||
void activateStrobeMem(int slotNum)
|
||||
{
|
||||
if (intC8ROM) return;
|
||||
|
||||
super.activateStrobeMem(slotNum);
|
||||
}
|
||||
|
||||
void deactivateStrobeMem()
|
||||
{
|
||||
intC8ROM = false;
|
||||
super.deactivateStrobeMem();
|
||||
}
|
||||
|
||||
ubyte readIntCXROM()
|
||||
{
|
||||
return kbdLatch() | (intCXROM ? 0x80 : 0x00);
|
||||
}
|
||||
|
||||
ubyte readSlotC3ROM()
|
||||
{
|
||||
return kbdLatch() | (slotC3ROM ? 0x80 : 0x00);
|
||||
}
|
||||
|
||||
mixin(InitSwitches("", [
|
||||
mixin(MakeSwitch([0xC006], "W", "resetIntCXROM")),
|
||||
mixin(MakeSwitch([0xC007], "W", "setIntCXROM")),
|
||||
mixin(MakeSwitch([0xC00A], "W", "resetSlotC3ROM")),
|
||||
mixin(MakeSwitch([0xC00B], "W", "setSlotC3ROM")),
|
||||
mixin(MakeSwitch([0xC015], "R", "readIntCXROM")),
|
||||
mixin(MakeSwitch([0xC017], "R", "readSlotC3ROM"))
|
||||
]));
|
||||
|
||||
void setRom(ubyte[] romDump)
|
||||
{
|
||||
int c100 = romDump.length - 16128;
|
||||
c100c2ff = new Rom(0xC100, 0x0200, romDump[c100 .. (c100 + 0x0200)]);
|
||||
intC3ROM = new IntC3ROM(romDump[(c100 + 0x0200) .. (c100 + 0x0300)]);
|
||||
c400c7ff = new Rom(0xC400, 0x0400,
|
||||
romDump[(c100 + 0x0300) .. (c100 + 0x0700)]);
|
||||
// XXX not slot, but ref to intc3rom
|
||||
intStrobeMem = new IOStrobeMem(3,
|
||||
romDump[(c100 + 0x0700) .. (c100 + 0x0F00)]);
|
||||
c100c2ff.debugName = intC3ROM.debugName = c400c7ff.debugName =
|
||||
intStrobeMem.debugName = "Internal ROM";
|
||||
}
|
||||
}
|
||||
|
407
src/ioummu.d
Normal file
407
src/ioummu.d
Normal file
@ -0,0 +1,407 @@
|
||||
/+
|
||||
+ ioummu.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 memory;
|
||||
import device.base;
|
||||
import system.io; // XXX XXX too many dependencies?
|
||||
import video.base;
|
||||
import peripheral.langcard;
|
||||
import iomem;
|
||||
|
||||
struct MemMap
|
||||
{
|
||||
Memory X0000_01FF;
|
||||
Memory X0200_03FF;
|
||||
Memory X0400_07FF;
|
||||
Memory X0800_1FFF;
|
||||
Memory X2000_3FFF;
|
||||
Memory X4000_BFFF;
|
||||
|
||||
void reboot()
|
||||
{
|
||||
X0000_01FF.reboot();
|
||||
X0200_03FF.reboot();
|
||||
X0400_07FF.reboot();
|
||||
X0800_1FFF.reboot();
|
||||
X2000_3FFF.reboot();
|
||||
X4000_BFFF.reboot();
|
||||
}
|
||||
}
|
||||
|
||||
class IOU
|
||||
{
|
||||
IO io_;
|
||||
SignalBase signal;
|
||||
bool dHGR;
|
||||
|
||||
this(IO ioDevices, SignalBase sig)
|
||||
{
|
||||
io_ = ioDevices;
|
||||
signal = sig;
|
||||
}
|
||||
|
||||
void resetAn0()
|
||||
{
|
||||
io_.annun.ann_0_Off();
|
||||
}
|
||||
|
||||
void resetAn1()
|
||||
{
|
||||
io_.annun.ann_1_Off();
|
||||
}
|
||||
|
||||
void resetAn2()
|
||||
{
|
||||
io_.annun.ann_2_Off();
|
||||
}
|
||||
|
||||
void resetAn3()
|
||||
{
|
||||
io_.annun.ann_3_Off();
|
||||
dHGR = true;
|
||||
signal.dHGRChange(true);
|
||||
}
|
||||
|
||||
void setAn0()
|
||||
{
|
||||
io_.annun.ann_0_On();
|
||||
}
|
||||
|
||||
void setAn1()
|
||||
{
|
||||
io_.annun.ann_1_On();
|
||||
}
|
||||
|
||||
void setAn2()
|
||||
{
|
||||
io_.annun.ann_2_On();
|
||||
}
|
||||
|
||||
void setAn3()
|
||||
{
|
||||
io_.annun.ann_3_On();
|
||||
dHGR = false;
|
||||
signal.dHGRChange(false);
|
||||
}
|
||||
|
||||
ubyte readDHGR()
|
||||
{
|
||||
io_.paddles.trigger();
|
||||
return (dHGR ? 0x80 : 0x00);
|
||||
}
|
||||
|
||||
void triggerTimers()
|
||||
{
|
||||
io_.paddles.trigger();
|
||||
}
|
||||
|
||||
mixin(InitSwitches("", [
|
||||
mixin(MakeSwitch([0xC058], "R0W", "resetAn0")),
|
||||
mixin(MakeSwitch([0xC059], "R0W", "setAn0")),
|
||||
mixin(MakeSwitch([0xC05A], "R0W", "resetAn1")),
|
||||
mixin(MakeSwitch([0xC05B], "R0W", "setAn1")),
|
||||
mixin(MakeSwitch([0xC05C], "R0W", "resetAn2")),
|
||||
mixin(MakeSwitch([0xC05D], "R0W", "setAn2")),
|
||||
mixin(MakeSwitch([0xC05E], "R0W", "resetAn3")),
|
||||
mixin(MakeSwitch([0xC05F], "R0W", "setAn3")),
|
||||
// XXX next two only for enhanced:
|
||||
mixin(MakeSwitch([0xC07F], "R7", "readDHGR")),
|
||||
mixin(MakeSwitch([0xC07F], "W", "triggerTimers"))
|
||||
]));
|
||||
}
|
||||
|
||||
class MMU
|
||||
{
|
||||
LanguageCard_IIe himemManager;
|
||||
AddressDecoder decoder;
|
||||
AuxiliaryCard auxCard;
|
||||
IOMem_IIe ioMem;
|
||||
ScannerBase scanner;
|
||||
ubyte delegate() kbdLatch;
|
||||
|
||||
MemMap[2] mem;
|
||||
|
||||
this()
|
||||
{
|
||||
himemManager = new LanguageCard_IIe();
|
||||
}
|
||||
|
||||
void reboot()
|
||||
{
|
||||
himemManager.reboot();
|
||||
mem[0].reboot();
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
himemManager.reset();
|
||||
ioMem.reset();
|
||||
}
|
||||
|
||||
void init(SoftSwitchPage switches, AuxiliaryCard auxCard_,
|
||||
AddressDecoder addrDecode, DataMem mainRam, DataMem mainRom)
|
||||
{
|
||||
auxCard = auxCard_;
|
||||
decoder = addrDecode;
|
||||
|
||||
himemManager.auxslot = auxCard.himem;
|
||||
himemManager.initSwitches(switches);
|
||||
himemManager.init(decoder, &mainRom.read,
|
||||
&mainRom.write);
|
||||
|
||||
initMem(mainRam);
|
||||
|
||||
initSwitches(switches);
|
||||
ioMem.initSwitches(switches);
|
||||
}
|
||||
|
||||
void initMem(DataMem mainRam)
|
||||
{
|
||||
mem[0].X0000_01FF = new SliceMem(0x0000, 0x0200, mainRam);
|
||||
mem[0].X0200_03FF = new SliceMem(0x0200, 0x0200, mainRam);
|
||||
mem[0].X0400_07FF = new SliceMem(0x0400, 0x0400, mainRam);
|
||||
mem[0].X0800_1FFF = new SliceMem(0x0800, 0x1800, mainRam);
|
||||
mem[0].X2000_3FFF = new SliceMem(0x2000, 0x2000, mainRam);
|
||||
mem[0].X4000_BFFF = new SliceMem(0x4000, 0x8000, mainRam);
|
||||
mem[1] = auxCard.mem;
|
||||
}
|
||||
|
||||
void initIO(ScannerBase scn, ubyte delegate() peekLatch)
|
||||
{
|
||||
kbdLatch = peekLatch;
|
||||
scanner = scn;
|
||||
ioMem.kbdLatch = kbdLatch;
|
||||
himemManager.kbdLatch = kbdLatch;
|
||||
}
|
||||
|
||||
bool switchPage2, switchHires, switch80Store;
|
||||
bool ramRd, ramWrt, altZP;
|
||||
|
||||
void mapVidMem(bool rd, bool wrt)
|
||||
{
|
||||
decoder.installRead(mem[cast(int)rd].X0400_07FF);
|
||||
decoder.installWrite(mem[cast(int)wrt].X0400_07FF);
|
||||
if (switchHires)
|
||||
{
|
||||
mapHiresVidMem(rd, wrt);
|
||||
}
|
||||
}
|
||||
|
||||
void mapHiresVidMem(bool rd, bool wrt)
|
||||
{
|
||||
decoder.installRead(mem[cast(int)rd].X2000_3FFF);
|
||||
decoder.installWrite(mem[cast(int)wrt].X2000_3FFF);
|
||||
}
|
||||
|
||||
void resetRamRd()
|
||||
{
|
||||
ramRd = false;
|
||||
changeRamSwitch(ramRd, true, false);
|
||||
}
|
||||
|
||||
void setRamRd()
|
||||
{
|
||||
ramRd = true;
|
||||
changeRamSwitch(ramRd, true, false);
|
||||
}
|
||||
|
||||
void resetRamWrt()
|
||||
{
|
||||
ramWrt = false;
|
||||
changeRamSwitch(ramWrt, false, true);
|
||||
}
|
||||
|
||||
void setRamWrt()
|
||||
{
|
||||
ramWrt = true;
|
||||
changeRamSwitch(ramWrt, false, true);
|
||||
}
|
||||
|
||||
void changeRamSwitch(bool sw, bool rd, bool wrt)
|
||||
{
|
||||
int bank = cast(int)sw;
|
||||
decoder.install(mem[bank].X0200_03FF, rd, wrt);
|
||||
if (!switch80Store)
|
||||
decoder.install(mem[bank].X0400_07FF, rd, wrt);
|
||||
decoder.install(mem[bank].X0800_1FFF, rd, wrt);
|
||||
if ((!switch80Store) || (!switchHires))
|
||||
decoder.install(mem[bank].X2000_3FFF, rd, wrt);
|
||||
decoder.install(mem[bank].X4000_BFFF, rd, wrt);
|
||||
}
|
||||
|
||||
void resetAltZP()
|
||||
{
|
||||
altZP = false;
|
||||
himemManager.enableAuxSlot(false);
|
||||
decoder.install(mem[0].X0000_01FF);
|
||||
}
|
||||
|
||||
void setAltZP()
|
||||
{
|
||||
altZP = true;
|
||||
himemManager.enableAuxSlot(true);
|
||||
decoder.install(mem[1].X0000_01FF);
|
||||
}
|
||||
|
||||
void reset80Store()
|
||||
{
|
||||
if (switchPage2)
|
||||
scanner.page2SwitchOn();
|
||||
else
|
||||
scanner.page2SwitchOff();
|
||||
switch80Store = false;
|
||||
mapVidMem(ramRd, ramWrt);
|
||||
}
|
||||
|
||||
void set80Store()
|
||||
{
|
||||
scanner.page2SwitchOff();
|
||||
switch80Store = true;
|
||||
mapVidMem(switchPage2, switchPage2);
|
||||
}
|
||||
|
||||
void resetPage2()
|
||||
{
|
||||
if (!switch80Store) scanner.page2SwitchOff();
|
||||
switchPage2 = false;
|
||||
if (switch80Store) mapVidMem(switchPage2, switchPage2);
|
||||
}
|
||||
|
||||
void setPage2()
|
||||
{
|
||||
if (!switch80Store) scanner.page2SwitchOn();
|
||||
switchPage2 = true;
|
||||
if (switch80Store) mapVidMem(switchPage2, switchPage2);
|
||||
}
|
||||
|
||||
void resetHires()
|
||||
{
|
||||
scanner.hiresSwitchOff();
|
||||
switchHires = false;
|
||||
if (switch80Store) mapHiresVidMem(ramRd, ramWrt);
|
||||
}
|
||||
|
||||
void setHires()
|
||||
{
|
||||
scanner.hiresSwitchOn();
|
||||
switchHires = true;
|
||||
if (switch80Store) mapHiresVidMem(switchPage2, switchPage2);
|
||||
}
|
||||
|
||||
ubyte readKbdSwitch(bool sw)
|
||||
{
|
||||
return kbdLatch() | (sw ? 0x80 : 0x00);
|
||||
}
|
||||
|
||||
ubyte readRamRd()
|
||||
{
|
||||
return readKbdSwitch(ramRd);
|
||||
}
|
||||
|
||||
ubyte readRamWrt()
|
||||
{
|
||||
return readKbdSwitch(ramWrt);
|
||||
}
|
||||
|
||||
ubyte readAltZP()
|
||||
{
|
||||
return readKbdSwitch(altZP);
|
||||
}
|
||||
|
||||
ubyte read80Store()
|
||||
{
|
||||
return readKbdSwitch(switch80Store);
|
||||
}
|
||||
|
||||
ubyte readPage2()
|
||||
{
|
||||
return readKbdSwitch(switchPage2);
|
||||
}
|
||||
|
||||
ubyte readHires()
|
||||
{
|
||||
return readKbdSwitch(switchHires);
|
||||
}
|
||||
|
||||
mixin(InitSwitches("", [
|
||||
mixin(MakeSwitch([0xC000], "W", "reset80Store")),
|
||||
mixin(MakeSwitch([0xC001], "W", "set80Store")),
|
||||
mixin(MakeSwitch([0xC002], "W", "resetRamRd")),
|
||||
mixin(MakeSwitch([0xC003], "W", "setRamRd")),
|
||||
mixin(MakeSwitch([0xC004], "W", "resetRamWrt")),
|
||||
mixin(MakeSwitch([0xC005], "W", "setRamWrt")),
|
||||
mixin(MakeSwitch([0xC008], "W", "resetAltZP")),
|
||||
mixin(MakeSwitch([0xC009], "W", "setAltZP")),
|
||||
mixin(MakeSwitch([0xC013], "R", "readRamRd")),
|
||||
mixin(MakeSwitch([0xC014], "R", "readRamWrt")),
|
||||
mixin(MakeSwitch([0xC016], "R", "readAltZP")),
|
||||
mixin(MakeSwitch([0xC018], "R", "read80Store")),
|
||||
mixin(MakeSwitch([0xC01C], "R", "readPage2")),
|
||||
mixin(MakeSwitch([0xC01D], "R", "readHires")),
|
||||
mixin(MakeSwitch([0xC054], "R0W", "resetPage2")),
|
||||
mixin(MakeSwitch([0xC055], "R0W", "setPage2")),
|
||||
mixin(MakeSwitch([0xC056], "R0W", "resetHires")),
|
||||
mixin(MakeSwitch([0xC057], "R0W", "setHires"))
|
||||
]));
|
||||
}
|
||||
|
||||
class AuxiliaryCard
|
||||
{
|
||||
HighRam_IIe himem;
|
||||
MemMap mem;
|
||||
VideoPages vidPages;
|
||||
abstract void reboot();
|
||||
abstract void reset();
|
||||
}
|
||||
|
||||
class Extended80ColumnCard : AuxiliaryCard
|
||||
{
|
||||
PrimaryMem auxRam;
|
||||
|
||||
this()
|
||||
{
|
||||
auxRam = new PrimaryMem(0x0000, 0xC000);
|
||||
auxRam.debugName = "Aux RAM";
|
||||
himem = new HighRam_IIe(false, 1);
|
||||
himem.setDebugName("Aux high RAM");
|
||||
vidPages.lores1 = new SliceMem(0x0400, 0x0400, auxRam);
|
||||
vidPages.lores2 = new SliceMem(0x0800, 0x0400, auxRam);
|
||||
vidPages.hires1 = new SliceMem(0x2000, 0x2000, auxRam);
|
||||
vidPages.hires2 = new SliceMem(0x4000, 0x2000, auxRam);
|
||||
mem.X0000_01FF = new SliceMem(0x0000, 0x0200, auxRam);
|
||||
mem.X0200_03FF = new SliceMem(0x0200, 0x0200, auxRam);
|
||||
mem.X0400_07FF = new SliceMem(0x0400, 0x0400, auxRam);
|
||||
mem.X0800_1FFF = new SliceMem(0x0800, 0x1800, auxRam);
|
||||
mem.X2000_3FFF = new SliceMem(0x2000, 0x2000, auxRam);
|
||||
mem.X4000_BFFF = new SliceMem(0x4000, 0x8000, auxRam);
|
||||
}
|
||||
|
||||
void reboot()
|
||||
{
|
||||
auxRam.reboot();
|
||||
vidPages.reboot();
|
||||
mem.reboot();
|
||||
}
|
||||
|
||||
void reset() {}
|
||||
}
|
||||
|
414
src/memory.d
Normal file
414
src/memory.d
Normal file
@ -0,0 +1,414 @@
|
||||
/+
|
||||
+ memory.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 std.string;
|
||||
|
||||
class Memory
|
||||
{
|
||||
final ushort baseAddress;
|
||||
final uint blockSize;
|
||||
string debugName;
|
||||
|
||||
this(ushort baseAddr, uint size)
|
||||
{
|
||||
assert(baseAddr + size <= 0x10000,
|
||||
"Memory block larger than 64K");
|
||||
assert((baseAddr % 0x0100) == 0,
|
||||
"Memory block does not start on page boundary");
|
||||
assert((size % 0x0100) == 0,
|
||||
"Memory block does not end on page boundary");
|
||||
baseAddress = baseAddr;
|
||||
blockSize = size;
|
||||
}
|
||||
|
||||
abstract ubyte read(ushort addr);
|
||||
abstract void write(ushort addr, ubyte val);
|
||||
void reboot() {}
|
||||
}
|
||||
|
||||
class ZeroMem : Memory
|
||||
{
|
||||
this(ushort baseAddr, uint size)
|
||||
{
|
||||
super(baseAddr, size);
|
||||
}
|
||||
|
||||
ubyte read(ushort addr) { return 0; }
|
||||
void write(ushort addr, ubyte val) {}
|
||||
}
|
||||
|
||||
class DataMem : Memory
|
||||
{
|
||||
ubyte* data;
|
||||
ubyte data_[];
|
||||
|
||||
this(ushort baseAddr, uint size)
|
||||
{
|
||||
super(baseAddr, size);
|
||||
}
|
||||
|
||||
ubyte read(ushort addr)
|
||||
{
|
||||
return data[addr - baseAddress];
|
||||
}
|
||||
|
||||
void write(ushort addr, ubyte val)
|
||||
{
|
||||
data[addr - baseAddress] = val;
|
||||
}
|
||||
}
|
||||
|
||||
class PrimaryMem : DataMem
|
||||
{
|
||||
this(ushort baseAddr, uint size)
|
||||
{
|
||||
super(baseAddr, size);
|
||||
}
|
||||
|
||||
void reboot()
|
||||
{
|
||||
data_ = new ubyte[blockSize];
|
||||
data = data_.ptr;
|
||||
}
|
||||
}
|
||||
|
||||
class Rom : DataMem
|
||||
{
|
||||
this(ushort baseAddr, uint size, ubyte[] rom)
|
||||
{
|
||||
super(baseAddr, size);
|
||||
data_ = rom;
|
||||
data = data_.ptr;
|
||||
}
|
||||
|
||||
void write(ushort addr, ubyte val) {}
|
||||
}
|
||||
|
||||
class SliceMem : DataMem
|
||||
{
|
||||
DataMem otherMem;
|
||||
|
||||
this(ushort baseAddr, uint size, DataMem other)
|
||||
{
|
||||
super(baseAddr, size);
|
||||
otherMem = other;
|
||||
debugName = otherMem.debugName;
|
||||
}
|
||||
|
||||
void reboot()
|
||||
{
|
||||
int otherStart = baseAddress - otherMem.baseAddress;
|
||||
int otherEnd = otherStart + blockSize;
|
||||
assert((otherStart >= 0) && (otherEnd <= otherMem.blockSize),
|
||||
"Memory slice out of range");
|
||||
data_ = otherMem.data_[otherStart..otherEnd];
|
||||
data = data_.ptr;
|
||||
}
|
||||
}
|
||||
|
||||
class BankMem : DataMem
|
||||
{
|
||||
ubyte[][] banks;
|
||||
string[] debugNames;
|
||||
|
||||
this(ushort baseAddr, uint size, uint numBanks)
|
||||
{
|
||||
super(baseAddr, size);
|
||||
banks.length = numBanks;
|
||||
debugNames.length = numBanks;
|
||||
}
|
||||
|
||||
void setDebugNames(string name)
|
||||
{
|
||||
if (debugNames.length > 1)
|
||||
{
|
||||
for (int n = 0; n < debugNames.length; ++n)
|
||||
{
|
||||
debugNames[n] = name ~ " bank " ~ std.string.toString(n);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
debugNames[0] = name;
|
||||
}
|
||||
}
|
||||
|
||||
void reboot()
|
||||
{
|
||||
for (int b = 0; b < banks.length; ++b)
|
||||
{
|
||||
banks[b] = new ubyte[blockSize];
|
||||
}
|
||||
setBank(0);
|
||||
}
|
||||
|
||||
void setBank(int bankNum)
|
||||
{
|
||||
data_ = banks[bankNum];
|
||||
data = data_.ptr;
|
||||
debugName = debugNames[bankNum];
|
||||
}
|
||||
}
|
||||
|
||||
class SubBankMem : DataMem
|
||||
{
|
||||
ubyte[][][] banks;
|
||||
string[][] debugNames;
|
||||
int primaryBank, subBank;
|
||||
int numBanks, numSubBanks;
|
||||
|
||||
this(ushort baseAddr, uint size, uint numBanks_, uint numSubBanks_)
|
||||
{
|
||||
super(baseAddr, size);
|
||||
banks.length = numBanks = numBanks_;
|
||||
debugNames.length = numBanks;
|
||||
numSubBanks = numSubBanks_;
|
||||
for (int b = 0; b < numBanks; ++b)
|
||||
{
|
||||
banks[b].length = numSubBanks_;
|
||||
debugNames[b].length = numSubBanks_;
|
||||
}
|
||||
}
|
||||
|
||||
void setDebugNames(string[] names)
|
||||
{
|
||||
for (int b = 0; b < numBanks; ++b)
|
||||
{
|
||||
for (int n = 0; n < numSubBanks; ++n)
|
||||
{
|
||||
debugNames[b][n] = names[b] ~ " bank " ~
|
||||
std.string.toString(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void reboot()
|
||||
{
|
||||
for (int b = 0; b < banks.length; ++b)
|
||||
{
|
||||
for (int s = 0; s < banks[b].length; ++s)
|
||||
{
|
||||
banks[b][s] = new ubyte[blockSize];
|
||||
}
|
||||
}
|
||||
primaryBank = subBank = 0;
|
||||
setSubBank(0);
|
||||
setPrimaryBank(0);
|
||||
}
|
||||
|
||||
void setPrimaryBank(uint bank)
|
||||
{
|
||||
primaryBank = bank;
|
||||
data_ = banks[bank][subBank];
|
||||
data = data_.ptr;
|
||||
debugName = debugNames[bank][subBank];
|
||||
}
|
||||
|
||||
void setSubBank(uint bank)
|
||||
{
|
||||
subBank = bank;
|
||||
data_ = banks[primaryBank][bank];
|
||||
data = data_.ptr;
|
||||
debugName = debugNames[primaryBank][bank];
|
||||
}
|
||||
}
|
||||
|
||||
alias ubyte delegate(ushort) ReadFunc;
|
||||
alias void delegate(ushort, ubyte) WriteFunc;
|
||||
|
||||
class AddressDecoder
|
||||
{
|
||||
ReadFunc readPages[256];
|
||||
WriteFunc writePages[256];
|
||||
Memory readResponders[256];
|
||||
Memory writeResponders[256];
|
||||
|
||||
void nullWrite(ushort addr, ubyte val) {}
|
||||
|
||||
public:
|
||||
|
||||
ubyte delegate(ushort) nullRead;
|
||||
|
||||
void installSwitches(SoftSwitchPage switches)
|
||||
{
|
||||
readPages[0xC0] = &switches.read;
|
||||
writePages[0xC0] = &switches.write;
|
||||
}
|
||||
|
||||
ubyte read(ushort addr)
|
||||
{
|
||||
return readPages[addr >> 8](addr);
|
||||
}
|
||||
|
||||
void write(ushort addr, ubyte val)
|
||||
{
|
||||
writePages[addr >> 8](addr, val);
|
||||
}
|
||||
|
||||
// XXX address read only/write only code
|
||||
void install(Memory block, bool forRead = true, bool forWrite = true)
|
||||
{
|
||||
uint base = block.baseAddress >> 8;
|
||||
uint size = block.blockSize >> 8;
|
||||
for (uint pg = base; pg < base + size; ++pg)
|
||||
{
|
||||
if (pg == 0xC0) continue;
|
||||
|
||||
if (forRead)
|
||||
{
|
||||
readPages[pg] = &block.read;
|
||||
readResponders[pg] = block;
|
||||
}
|
||||
if (forWrite)
|
||||
{
|
||||
writePages[pg] = &block.write;
|
||||
writeResponders[pg] = block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void installNull(uint baseAddress, uint blockSize, bool forRead = true,
|
||||
bool forWrite = true)
|
||||
{
|
||||
uint base = baseAddress >> 8;
|
||||
uint size = blockSize >> 8;
|
||||
for (uint pg = base; pg < base + size; ++pg)
|
||||
{
|
||||
if (pg == 0xC0) continue;
|
||||
if (forRead)
|
||||
{
|
||||
readPages[pg] = nullRead;
|
||||
readResponders[pg] = null;
|
||||
}
|
||||
if (forWrite)
|
||||
{
|
||||
writePages[pg] = &nullWrite;
|
||||
writeResponders[pg] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void installRead(Memory block)
|
||||
{
|
||||
install(block, true, false);
|
||||
}
|
||||
|
||||
void installWrite(Memory block)
|
||||
{
|
||||
install(block, false, true);
|
||||
}
|
||||
|
||||
string memoryReadName(ushort addr)
|
||||
{
|
||||
int page = addr >> 8;
|
||||
if (readResponders[page] is null) return null;
|
||||
return readResponders[page].debugName;
|
||||
}
|
||||
|
||||
Memory readResponse(int page)
|
||||
{
|
||||
return readResponders[page];
|
||||
}
|
||||
|
||||
Memory writeResponse(int page)
|
||||
{
|
||||
return writeResponders[page];
|
||||
}
|
||||
}
|
||||
|
||||
class SoftSwitchPage : Memory
|
||||
{
|
||||
private:
|
||||
|
||||
ReadFunc[256] readSwitches;
|
||||
ubyte[256] bitsReturned;
|
||||
WriteFunc[256] writeSwitches;
|
||||
|
||||
ubyte nullRead(ushort addr) { return 0; }
|
||||
void nullWrite(ushort addr, ubyte val) {}
|
||||
|
||||
public:
|
||||
|
||||
ReadFunc floatingBus;
|
||||
|
||||
this()
|
||||
{
|
||||
super(0xC000, 0x0100);
|
||||
for (int addr = 0xC000; addr < 0xC100; ++addr)
|
||||
{
|
||||
writeSwitches[addr & 0xFF] = &nullWrite;
|
||||
}
|
||||
}
|
||||
|
||||
void setFloatingBus(ReadFunc floatingBus_)
|
||||
{
|
||||
floatingBus = floatingBus_;
|
||||
for (int addr = 0xC000; addr < 0xC100; ++addr)
|
||||
{
|
||||
if (readSwitches[addr & 0xFF] is null)
|
||||
readSwitches[addr & 0xFF] = floatingBus;
|
||||
}
|
||||
}
|
||||
|
||||
void setReadSwitch(ushort addr, ReadFunc read_, ubyte bitsReturned_)
|
||||
{
|
||||
readSwitches[addr - 0xC000] = read_;
|
||||
bitsReturned[addr - 0xC000] = bitsReturned_;
|
||||
}
|
||||
|
||||
void setR0Switch(ushort addr, ReadFunc read_)
|
||||
{
|
||||
setReadSwitch(addr, read_, 0);
|
||||
}
|
||||
|
||||
void setR7Switch(ushort addr, ReadFunc read_)
|
||||
{
|
||||
setReadSwitch(addr, read_, 0x80);
|
||||
}
|
||||
|
||||
void setRSwitch(ushort addr, ReadFunc read_)
|
||||
{
|
||||
setReadSwitch(addr, read_, 0xFF);
|
||||
}
|
||||
|
||||
void setWSwitch(ushort addr, WriteFunc write_)
|
||||
{
|
||||
writeSwitches[addr - 0xC000] = write_;
|
||||
}
|
||||
|
||||
final ubyte read(ushort addr)
|
||||
{
|
||||
ubyte ret = readSwitches[addr - 0xC000](addr);
|
||||
ubyte mask = bitsReturned[addr - 0xC000];
|
||||
if (mask < 0xFF)
|
||||
{
|
||||
ret = (ret & mask) | (floatingBus(addr) & (mask ^ 0xFF));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
final void write(ushort addr, ubyte val)
|
||||
{
|
||||
writeSwitches[addr - 0xC000](addr, val);
|
||||
}
|
||||
}
|
||||
|
59
src/peripheral/base.d
Normal file
59
src/peripheral/base.d
Normal file
@ -0,0 +1,59 @@
|
||||
/+
|
||||
+ peripheral/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 peripheral.base;
|
||||
|
||||
import memory;
|
||||
import device.base;
|
||||
import timer;
|
||||
|
||||
import gtk.Box;
|
||||
import gtk.VBox;
|
||||
|
||||
VBox peripheralStatus;
|
||||
|
||||
class Peripheral
|
||||
{
|
||||
Box statusBox;
|
||||
|
||||
ubyte[] ioSelectROM;
|
||||
ubyte[] ioStrobeROM;
|
||||
|
||||
void plugIn(int slotNum, SoftSwitchPage switches, Timer timer)
|
||||
{
|
||||
initSwitches(switches, slotNum);
|
||||
initTimer(timer);
|
||||
|
||||
if (statusBox !is null)
|
||||
{
|
||||
peripheralStatus.packStart(statusBox, false, false, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void reset() {}
|
||||
void reboot() {}
|
||||
void updateStatus() {}
|
||||
void initTimer(Timer timer) {}
|
||||
|
||||
mixin(EmptyInitSwitches());
|
||||
}
|
||||
|
1144
src/peripheral/diskii.d
Normal file
1144
src/peripheral/diskii.d
Normal file
File diff suppressed because it is too large
Load Diff
430
src/peripheral/langcard.d
Normal file
430
src/peripheral/langcard.d
Normal file
@ -0,0 +1,430 @@
|
||||
/+
|
||||
+ peripheral/langcard.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 peripheral.langcard;
|
||||
|
||||
import peripheral.base;
|
||||
import device.base;
|
||||
import memory;
|
||||
|
||||
private:
|
||||
|
||||
string MakeRAMSwitches(bool isIIe)
|
||||
{
|
||||
string ramSwitches;
|
||||
for (int sw = 0xC080; sw <= 0xC08F; ++sw)
|
||||
{
|
||||
ramSwitches ~= "void read" ~ hex4Digits(sw) ~ "()\n{\n" ~
|
||||
MakeRAMSwitch(sw, false, isIIe) ~ "}\n";
|
||||
ramSwitches ~= "void write" ~ hex4Digits(sw) ~ "()\n{\n" ~
|
||||
MakeRAMSwitch(sw, true, isIIe) ~ "}\n";
|
||||
}
|
||||
return ramSwitches;
|
||||
}
|
||||
|
||||
string MakeRAMSwitch(int sw, bool write, bool isIIe)
|
||||
{
|
||||
string ramSwitch;
|
||||
|
||||
ramSwitch ~= CallRAMFunction(isIIe, "setBank(" ~
|
||||
hex4Digits((sw & 0b1000) >> 3 ) ~ ")") ~ "\n";
|
||||
|
||||
if (((sw & 0b11) == 0b11) || ((sw & 0b11) == 0b00))
|
||||
ramSwitch ~= CallRAMFunction(isIIe, "enableRead()") ~ "\n";
|
||||
else
|
||||
ramSwitch ~= CallRAMFunction(isIIe, "disableRead()") ~ "\n";
|
||||
|
||||
if ((sw & 0b1) == 0b0)
|
||||
ramSwitch ~= CallRAMFunction(isIIe, "clearPreWrite()") ~ "\n" ~
|
||||
CallRAMFunction(isIIe, "disableWrite()") ~ "\n";
|
||||
else
|
||||
{
|
||||
if (write)
|
||||
ramSwitch ~= CallRAMFunction(isIIe, "clearPreWrite()") ~ "\n";
|
||||
else
|
||||
ramSwitch ~= CallRAMFunction(isIIe, "checkEnableWrite()") ~ "\n";
|
||||
}
|
||||
|
||||
return ramSwitch;
|
||||
}
|
||||
|
||||
string CallRAMFunction(bool isIIe, string func)
|
||||
{
|
||||
if (!isIIe)
|
||||
{
|
||||
return "implementation." ~ func ~ ";";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "onboard." ~ func ~ ";\n" ~
|
||||
"if (auxslot !is null) auxslot." ~ func ~ ";";
|
||||
}
|
||||
}
|
||||
|
||||
static const string[][] langcardSwitches = [
|
||||
mixin(MakeSwitch([0xC080], "R0", "readC080")),
|
||||
mixin(MakeSwitch([0xC081], "R0", "readC081")),
|
||||
mixin(MakeSwitch([0xC082], "R0", "readC082")),
|
||||
mixin(MakeSwitch([0xC083], "R0", "readC083")),
|
||||
mixin(MakeSwitch([0xC084], "R0", "readC084")),
|
||||
mixin(MakeSwitch([0xC085], "R0", "readC085")),
|
||||
mixin(MakeSwitch([0xC086], "R0", "readC086")),
|
||||
mixin(MakeSwitch([0xC087], "R0", "readC087")),
|
||||
mixin(MakeSwitch([0xC088], "R0", "readC088")),
|
||||
mixin(MakeSwitch([0xC089], "R0", "readC089")),
|
||||
mixin(MakeSwitch([0xC08A], "R0", "readC08A")),
|
||||
mixin(MakeSwitch([0xC08B], "R0", "readC08B")),
|
||||
mixin(MakeSwitch([0xC08C], "R0", "readC08C")),
|
||||
mixin(MakeSwitch([0xC08D], "R0", "readC08D")),
|
||||
mixin(MakeSwitch([0xC08E], "R0", "readC08E")),
|
||||
mixin(MakeSwitch([0xC08F], "R0", "readC08F")),
|
||||
mixin(MakeSwitch([0xC080], "W", "writeC080")),
|
||||
mixin(MakeSwitch([0xC081], "W", "writeC081")),
|
||||
mixin(MakeSwitch([0xC082], "W", "writeC082")),
|
||||
mixin(MakeSwitch([0xC083], "W", "writeC083")),
|
||||
mixin(MakeSwitch([0xC084], "W", "writeC084")),
|
||||
mixin(MakeSwitch([0xC085], "W", "writeC085")),
|
||||
mixin(MakeSwitch([0xC086], "W", "writeC086")),
|
||||
mixin(MakeSwitch([0xC087], "W", "writeC087")),
|
||||
mixin(MakeSwitch([0xC088], "W", "writeC088")),
|
||||
mixin(MakeSwitch([0xC089], "W", "writeC089")),
|
||||
mixin(MakeSwitch([0xC08A], "W", "writeC08A")),
|
||||
mixin(MakeSwitch([0xC08B], "W", "writeC08B")),
|
||||
mixin(MakeSwitch([0xC08C], "W", "writeC08C")),
|
||||
mixin(MakeSwitch([0xC08D], "W", "writeC08D")),
|
||||
mixin(MakeSwitch([0xC08E], "W", "writeC08E")),
|
||||
mixin(MakeSwitch([0xC08F], "W", "writeC08F"))
|
||||
];
|
||||
|
||||
public:
|
||||
|
||||
class HighRam
|
||||
{
|
||||
AddressDecoder decoder;
|
||||
|
||||
ReadFunc origRead;
|
||||
WriteFunc origWrite;
|
||||
|
||||
Memory lowerChunk, upperChunk;
|
||||
|
||||
bool preWrite;
|
||||
bool readEn, writeEn;
|
||||
bool enabled;
|
||||
int current4KBank;
|
||||
|
||||
this()
|
||||
{
|
||||
initMemory();
|
||||
}
|
||||
|
||||
abstract void initMemory();
|
||||
|
||||
void init(AddressDecoder addrDecode, ReadFunc read, WriteFunc write)
|
||||
{
|
||||
decoder = addrDecode;
|
||||
origRead = read;
|
||||
origWrite = write;
|
||||
}
|
||||
|
||||
void reboot()
|
||||
{
|
||||
preWrite = false;
|
||||
lowerChunk.reboot();
|
||||
upperChunk.reboot();
|
||||
current4KBank = 0;
|
||||
forceReadDisable();
|
||||
forceWriteEnable();
|
||||
}
|
||||
|
||||
void clearPreWrite()
|
||||
{
|
||||
preWrite = false;
|
||||
}
|
||||
|
||||
void forceReadDisable()
|
||||
{
|
||||
readEn = true;
|
||||
disableRead();
|
||||
}
|
||||
|
||||
void forceReadEnable()
|
||||
{
|
||||
readEn = false;
|
||||
enableRead();
|
||||
}
|
||||
|
||||
void forceWriteEnable()
|
||||
{
|
||||
writeEn = false;
|
||||
enableWrite();
|
||||
}
|
||||
|
||||
void enableRead()
|
||||
{
|
||||
if ((!readEn) && enabled)
|
||||
{
|
||||
decoder.installRead(lowerChunk);
|
||||
decoder.installRead(upperChunk);
|
||||
}
|
||||
readEn = true;
|
||||
}
|
||||
|
||||
void disableRead()
|
||||
{
|
||||
if (readEn && enabled)
|
||||
{
|
||||
decoder.readPages[0xD0..0x100] = origRead;
|
||||
}
|
||||
readEn = false;
|
||||
}
|
||||
|
||||
void checkEnableWrite()
|
||||
{
|
||||
if (preWrite) enableWrite();
|
||||
preWrite = true;
|
||||
}
|
||||
|
||||
void enableWrite()
|
||||
{
|
||||
if ((!writeEn) && enabled)
|
||||
{
|
||||
decoder.installWrite(lowerChunk);
|
||||
decoder.installWrite(upperChunk);
|
||||
}
|
||||
writeEn = true;
|
||||
}
|
||||
|
||||
void disableWrite()
|
||||
{
|
||||
if (writeEn && enabled)
|
||||
{
|
||||
decoder.writePages[0xD0..0x100] = origWrite;
|
||||
}
|
||||
writeEn = false;
|
||||
}
|
||||
|
||||
abstract void setLowerBank(int bank);
|
||||
|
||||
void setBank(int bank)
|
||||
{
|
||||
setLowerBank(bank);
|
||||
current4KBank = bank;
|
||||
}
|
||||
}
|
||||
|
||||
class HighRam_II : HighRam
|
||||
{
|
||||
PrimaryMem e000ffff;
|
||||
BankMem d000dfff;
|
||||
|
||||
this()
|
||||
{
|
||||
super();
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
void initMemory()
|
||||
{
|
||||
d000dfff = new BankMem(0xD000, 0x1000, 2);
|
||||
e000ffff = new PrimaryMem(0xE000, 0x2000);
|
||||
lowerChunk = d000dfff;
|
||||
upperChunk = e000ffff;
|
||||
}
|
||||
|
||||
void setDebugName(string name)
|
||||
{
|
||||
e000ffff.debugName = name;
|
||||
d000dfff.setDebugNames(name);
|
||||
}
|
||||
|
||||
void setLowerBank(int bank)
|
||||
{
|
||||
d000dfff.setBank(bank);
|
||||
}
|
||||
}
|
||||
|
||||
class HighRam_IIe : HighRam
|
||||
{
|
||||
bool isOnboard;
|
||||
int numBanks;
|
||||
SubBankMem d000dfff;
|
||||
BankMem e000ffff;
|
||||
|
||||
this(bool isOnboard_, int banks)
|
||||
{
|
||||
numBanks = banks;
|
||||
super();
|
||||
isOnboard = isOnboard_;
|
||||
}
|
||||
|
||||
void initMemory()
|
||||
{
|
||||
d000dfff = new SubBankMem(0xD000, 0x1000, numBanks, 2);
|
||||
e000ffff = new BankMem(0xE000, 0x2000, numBanks);
|
||||
lowerChunk = d000dfff;
|
||||
upperChunk = e000ffff;
|
||||
}
|
||||
|
||||
void setLowerBank(int bank)
|
||||
{
|
||||
d000dfff.setSubBank(bank);
|
||||
}
|
||||
|
||||
void resetBanks()
|
||||
{
|
||||
e000ffff.setBank(0);
|
||||
d000dfff.setPrimaryBank(0);
|
||||
d000dfff.setSubBank(0);
|
||||
}
|
||||
|
||||
void enable(bool newState)
|
||||
{
|
||||
if (enabled == newState) return;
|
||||
enabled = newState;
|
||||
if (newState)
|
||||
{
|
||||
if (writeEn) forceWriteEnable();
|
||||
if (readEn) forceReadEnable();
|
||||
}
|
||||
}
|
||||
|
||||
void setDebugName(string name)
|
||||
{
|
||||
string[] names;
|
||||
names.length = numBanks;
|
||||
if (numBanks > 1)
|
||||
{
|
||||
for (int b = 0; b < numBanks; ++b)
|
||||
names[b] = name ~ " " ~ std.string.toString(b);
|
||||
}
|
||||
else
|
||||
{
|
||||
names[0] = name;
|
||||
}
|
||||
e000ffff.setDebugNames(name);
|
||||
d000dfff.setDebugNames(names);
|
||||
}
|
||||
|
||||
void reboot()
|
||||
{
|
||||
enabled = isOnboard;
|
||||
super.reboot();
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
enabled = isOnboard;
|
||||
preWrite = false;
|
||||
resetBanks();
|
||||
current4KBank = 0;
|
||||
disableRead();
|
||||
enableWrite();
|
||||
}
|
||||
}
|
||||
|
||||
class HighRam_Null : HighRam_IIe
|
||||
{
|
||||
this()
|
||||
{
|
||||
super(false, 0);
|
||||
}
|
||||
|
||||
void initMemory()
|
||||
{
|
||||
lowerChunk = new ZeroMem(0xD000, 0x1000);
|
||||
upperChunk = new ZeroMem(0xE000, 0x2000);
|
||||
}
|
||||
|
||||
void setLowerBank(int bank) {}
|
||||
void resetBanks() {}
|
||||
}
|
||||
|
||||
class LanguageCard : Peripheral
|
||||
{
|
||||
HighRam_II implementation;
|
||||
|
||||
mixin(MakeRAMSwitches(false));
|
||||
mixin(InitSwitches("", langcardSwitches));
|
||||
|
||||
this()
|
||||
{
|
||||
implementation = new HighRam_II();
|
||||
implementation.setDebugName("Language card");
|
||||
}
|
||||
|
||||
void reboot()
|
||||
{
|
||||
implementation.reboot();
|
||||
}
|
||||
}
|
||||
|
||||
class LanguageCard_IIe
|
||||
{
|
||||
HighRam_IIe onboard, auxslot;
|
||||
ubyte delegate() kbdLatch;
|
||||
|
||||
mixin(MakeRAMSwitches(true));
|
||||
mixin(InitSwitches("", langcardSwitches ~ [
|
||||
mixin(MakeSwitch([0xC011], "R", "readBank2Switch")),
|
||||
mixin(MakeSwitch([0xC012], "R", "readReadSwitch"))
|
||||
]));
|
||||
|
||||
this()
|
||||
{
|
||||
onboard = new HighRam_IIe(true, 1);
|
||||
onboard.setDebugName("High RAM");
|
||||
}
|
||||
|
||||
void init(AddressDecoder addrDecode, ReadFunc read, WriteFunc write)
|
||||
{
|
||||
onboard.init(addrDecode, read, write);
|
||||
auxslot.init(addrDecode, read, write);
|
||||
}
|
||||
|
||||
void reboot()
|
||||
{
|
||||
onboard.reboot();
|
||||
auxslot.reboot();
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
onboard.reset();
|
||||
auxslot.reset();
|
||||
}
|
||||
|
||||
void enableAuxSlot(bool isAuxSlot)
|
||||
{
|
||||
onboard.enable(!isAuxSlot);
|
||||
auxslot.enable(isAuxSlot);
|
||||
}
|
||||
|
||||
ubyte readBank2Switch()
|
||||
{
|
||||
return kbdLatch() | ((onboard.current4KBank == 0) ? 0x80 : 0x00);
|
||||
}
|
||||
|
||||
ubyte readReadSwitch()
|
||||
{
|
||||
return kbdLatch() | (onboard.readEn ? 0x80 : 0x00);
|
||||
}
|
||||
}
|
171
src/peripheral/saturn128.d
Normal file
171
src/peripheral/saturn128.d
Normal file
@ -0,0 +1,171 @@
|
||||
/+
|
||||
+ peripheral/saturn128.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 peripheral.saturn128;
|
||||
|
||||
import device.base;
|
||||
import peripheral.base;
|
||||
import memory;
|
||||
|
||||
string MakeSaturnSwitches()
|
||||
{
|
||||
string saturnSwitches;
|
||||
for (int sw = 0xC080; sw <= 0xC08F; ++sw)
|
||||
{
|
||||
saturnSwitches ~= "void access" ~ hex4Digits(sw) ~ "()\n{\n";
|
||||
if (sw & 0b00000100)
|
||||
saturnSwitches ~= MakeBankSwitch(sw);
|
||||
else
|
||||
saturnSwitches ~= MakeModeSwitch(sw);
|
||||
saturnSwitches ~= "}\n";
|
||||
}
|
||||
return saturnSwitches;
|
||||
}
|
||||
|
||||
string MakeModeSwitch(int sw)
|
||||
{
|
||||
string modeSwitch;
|
||||
modeSwitch ~= "set4KBank(" ~ hex2Digits((sw & 0b1000) >> 3) ~ ");\n";
|
||||
modeSwitch ~= (sw & 0b1) ? "tryWriteEnable();\n" : "writeProtect();\n";
|
||||
modeSwitch ~= (((sw & 0b11) == 0b00) || ((sw & 0b11) == 0b11)) ?
|
||||
"readRAM();\n" : "readROM();\n";
|
||||
return modeSwitch;
|
||||
}
|
||||
|
||||
string MakeBankSwitch(int sw)
|
||||
{
|
||||
int bank = (sw & 0b11) | ((sw & 0b1000) >> 1);
|
||||
return "set16KBank(" ~ hex2Digits(bank) ~ ");\n";
|
||||
}
|
||||
|
||||
class Saturn128 : Peripheral
|
||||
{
|
||||
AddressDecoder decoder;
|
||||
|
||||
ReadFunc origRead;
|
||||
WriteFunc origWrite;
|
||||
bool preWrite;
|
||||
bool readEn, writeEn;
|
||||
BankMem e000ffff;
|
||||
SubBankMem d000dfff;
|
||||
|
||||
this()
|
||||
{
|
||||
e000ffff = new BankMem(0xE000, 0x2000, 16);
|
||||
d000dfff = new SubBankMem(0xD000, 0x1000, 16, 2);
|
||||
}
|
||||
|
||||
void init(AddressDecoder addrDecode, ReadFunc read, WriteFunc write)
|
||||
{
|
||||
decoder = addrDecode;
|
||||
origRead = read;
|
||||
origWrite = write;
|
||||
}
|
||||
|
||||
void reboot()
|
||||
{
|
||||
preWrite = false;
|
||||
e000ffff.reboot();
|
||||
d000dfff.reboot();
|
||||
readEn = true;
|
||||
readROM();
|
||||
writeEn = true;
|
||||
writeProtect();
|
||||
}
|
||||
|
||||
void writeProtect()
|
||||
{
|
||||
if (writeEn)
|
||||
{
|
||||
decoder.writePages[0xD0..0x100] = origWrite;
|
||||
}
|
||||
preWrite = false;
|
||||
writeEn = false;
|
||||
}
|
||||
|
||||
void tryWriteEnable()
|
||||
{
|
||||
if (preWrite) writeEnable();
|
||||
preWrite = true;
|
||||
}
|
||||
|
||||
void writeEnable()
|
||||
{
|
||||
if (!writeEn)
|
||||
{
|
||||
decoder.installWrite(e000ffff);
|
||||
decoder.installWrite(d000dfff);
|
||||
}
|
||||
writeEn = true;
|
||||
}
|
||||
|
||||
void readRAM()
|
||||
{
|
||||
if (!readEn)
|
||||
{
|
||||
decoder.installRead(e000ffff);
|
||||
decoder.installRead(d000dfff);
|
||||
}
|
||||
readEn = true;
|
||||
}
|
||||
|
||||
void readROM()
|
||||
{
|
||||
if (readEn)
|
||||
{
|
||||
decoder.readPages[0xD0..0x100] = origRead;
|
||||
}
|
||||
readEn = false;
|
||||
}
|
||||
|
||||
void set16KBank(int bank)
|
||||
{
|
||||
e000ffff.setBank(bank);
|
||||
d000dfff.setPrimaryBank(bank);
|
||||
}
|
||||
|
||||
void set4KBank(int bank)
|
||||
{
|
||||
d000dfff.setSubBank(bank);
|
||||
}
|
||||
|
||||
mixin(MakeSaturnSwitches());
|
||||
|
||||
mixin(InitSwitches("", [
|
||||
mixin(MakeSwitch([0xC080], "R0W", "accessC080")),
|
||||
mixin(MakeSwitch([0xC081], "R0W", "accessC081")),
|
||||
mixin(MakeSwitch([0xC082], "R0W", "accessC082")),
|
||||
mixin(MakeSwitch([0xC083], "R0W", "accessC083")),
|
||||
mixin(MakeSwitch([0xC084], "R0W", "accessC084")),
|
||||
mixin(MakeSwitch([0xC085], "R0W", "accessC085")),
|
||||
mixin(MakeSwitch([0xC086], "R0W", "accessC086")),
|
||||
mixin(MakeSwitch([0xC087], "R0W", "accessC087")),
|
||||
mixin(MakeSwitch([0xC088], "R0W", "accessC088")),
|
||||
mixin(MakeSwitch([0xC089], "R0W", "accessC089")),
|
||||
mixin(MakeSwitch([0xC08A], "R0W", "accessC08A")),
|
||||
mixin(MakeSwitch([0xC08B], "R0W", "accessC08B")),
|
||||
mixin(MakeSwitch([0xC08C], "R0W", "accessC08C")),
|
||||
mixin(MakeSwitch([0xC08D], "R0W", "accessC08D")),
|
||||
mixin(MakeSwitch([0xC08E], "R0W", "accessC08E")),
|
||||
mixin(MakeSwitch([0xC08F], "R0W", "accessC08F"))
|
||||
]));
|
||||
}
|
653
src/sndfile.d
Normal file
653
src/sndfile.d
Normal file
@ -0,0 +1,653 @@
|
||||
module sndfile;
|
||||
|
||||
import derelict.util.loader;
|
||||
|
||||
/+
|
||||
+ The following file types can be read and written.
|
||||
+ A file type would consist of a major type (ie SF_FORMAT_WAV) bitwise
|
||||
+ ORed with a minor type (ie SF_FORMAT_PCM). SF_FORMAT_TYPEMASK and
|
||||
+ SF_FORMAT_SUBMASK can be used to separate the major and minor file
|
||||
+ types.
|
||||
+/
|
||||
|
||||
enum
|
||||
{
|
||||
// Major formats.
|
||||
SF_FORMAT_WAV = 0x010000, /+ Microsoft WAV format
|
||||
(little endian default). +/
|
||||
SF_FORMAT_AIFF = 0x020000, // Apple/SGI AIFF format (big endian).
|
||||
SF_FORMAT_AU = 0x030000, // Sun/NeXT AU format (big endian).
|
||||
SF_FORMAT_RAW = 0x040000, // RAW PCM data.
|
||||
SF_FORMAT_PAF = 0x050000, // Ensoniq PARIS file format.
|
||||
SF_FORMAT_SVX = 0x060000, // Amiga IFF / SVX8 / SV16 format.
|
||||
SF_FORMAT_NIST = 0x070000, // Sphere NIST format.
|
||||
SF_FORMAT_VOC = 0x080000, // VOC files.
|
||||
SF_FORMAT_IRCAM = 0x0A0000, // Berkeley/IRCAM/CARL
|
||||
SF_FORMAT_W64 = 0x0B0000, // Sonic Foundry's 64 bit RIFF/WAV
|
||||
SF_FORMAT_MAT4 = 0x0C0000, // Matlab (tm) V4.2 / GNU Octave 2.0
|
||||
SF_FORMAT_MAT5 = 0x0D0000, // Matlab (tm) V5.0 / GNU Octave 2.1
|
||||
SF_FORMAT_PVF = 0x0E0000, // Portable Voice Format
|
||||
SF_FORMAT_XI = 0x0F0000, // Fasttracker 2 Extended Instrument
|
||||
SF_FORMAT_HTK = 0x100000, // HMM Tool Kit format
|
||||
SF_FORMAT_SDS = 0x110000, // Midi Sample Dump Standard
|
||||
SF_FORMAT_AVR = 0x120000, // Audio Visual Research
|
||||
SF_FORMAT_WAVEX = 0x130000, // MS WAVE with WAVEFORMATEX
|
||||
SF_FORMAT_SD2 = 0x160000, // Sound Designer 2
|
||||
SF_FORMAT_FLAC = 0x170000, // FLAC lossless file format
|
||||
SF_FORMAT_CAF = 0x180000, // Core Audio File format
|
||||
|
||||
// Subtypes from here on.
|
||||
|
||||
SF_FORMAT_PCM_S8 = 0x0001, // Signed 8 bit data
|
||||
SF_FORMAT_PCM_16 = 0x0002, // Signed 16 bit data
|
||||
SF_FORMAT_PCM_24 = 0x0003, // Signed 24 bit data
|
||||
SF_FORMAT_PCM_32 = 0x0004, // Signed 32 bit data
|
||||
|
||||
SF_FORMAT_PCM_U8 = 0x0005, /+ Unsigned 8 bit data
|
||||
(WAV and RAW only) +/
|
||||
|
||||
SF_FORMAT_FLOAT = 0x0006, // 32 bit float data
|
||||
SF_FORMAT_DOUBLE = 0x0007, // 64 bit float data
|
||||
|
||||
SF_FORMAT_ULAW = 0x0010, // U-Law encoded.
|
||||
SF_FORMAT_ALAW = 0x0011, // A-Law encoded.
|
||||
SF_FORMAT_IMA_ADPCM = 0x0012, // IMA ADPCM.
|
||||
SF_FORMAT_MS_ADPCM = 0x0013, // Microsoft ADPCM.
|
||||
|
||||
SF_FORMAT_GSM610 = 0x0020, // GSM 6.10 encoding.
|
||||
SF_FORMAT_VOX_ADPCM = 0x0021, // OKI / Dialogix ADPCM
|
||||
|
||||
SF_FORMAT_G721_32 = 0x0030, // 32kbs G721 ADPCM encoding.
|
||||
SF_FORMAT_G723_24 = 0x0031, // 24kbs G723 ADPCM encoding.
|
||||
SF_FORMAT_G723_40 = 0x0032, // 40kbs G723 ADPCM encoding.
|
||||
|
||||
SF_FORMAT_DWVW_12 = 0x0040, /+ 12 bit Delta Width Variable Word
|
||||
encoding. +/
|
||||
SF_FORMAT_DWVW_16 = 0x0041, /+ 16 bit Delta Width Variable Word
|
||||
encoding. +/
|
||||
SF_FORMAT_DWVW_24 = 0x0042, /+ 24 bit Delta Width Variable Word
|
||||
encoding. +/
|
||||
SF_FORMAT_DWVW_N = 0x0043, /+ N bit Delta Width Variable Word
|
||||
encoding. +/
|
||||
|
||||
SF_FORMAT_DPCM_8 = 0x0050, // 8 bit differential PCM (XI only)
|
||||
SF_FORMAT_DPCM_16 = 0x0051, // 16 bit differential PCM (XI only)
|
||||
|
||||
// Endian-ness options.
|
||||
|
||||
SF_ENDIAN_FILE = 0x00000000, // Default file endian-ness.
|
||||
SF_ENDIAN_LITTLE = 0x10000000, // Force little endian-ness.
|
||||
SF_ENDIAN_BIG = 0x20000000, // Force big endian-ness.
|
||||
SF_ENDIAN_CPU = 0x30000000, // Force CPU endian-ness.
|
||||
|
||||
SF_FORMAT_SUBMASK = 0x0000FFFF,
|
||||
SF_FORMAT_TYPEMASK = 0x0FFF0000,
|
||||
SF_FORMAT_ENDMASK = 0x30000000
|
||||
}
|
||||
|
||||
/+
|
||||
+ The following are the valid command numbers for the sf_command()
|
||||
+ interface. The use of these commands is documented in the file
|
||||
+ command.html in the doc directory of the source code distribution.
|
||||
+/
|
||||
|
||||
enum
|
||||
{
|
||||
SFC_GET_LIB_VERSION = 0x1000,
|
||||
SFC_GET_LOG_INFO = 0x1001,
|
||||
|
||||
SFC_GET_NORM_DOUBLE = 0x1010,
|
||||
SFC_GET_NORM_FLOAT = 0x1011,
|
||||
SFC_SET_NORM_DOUBLE = 0x1012,
|
||||
SFC_SET_NORM_FLOAT = 0x1013,
|
||||
SFC_SET_SCALE_FLOAT_INT_READ = 0x1014,
|
||||
|
||||
SFC_GET_SIMPLE_FORMAT_COUNT = 0x1020,
|
||||
SFC_GET_SIMPLE_FORMAT = 0x1021,
|
||||
|
||||
SFC_GET_FORMAT_INFO = 0x1028,
|
||||
|
||||
SFC_GET_FORMAT_MAJOR_COUNT = 0x1030,
|
||||
SFC_GET_FORMAT_MAJOR = 0x1031,
|
||||
SFC_GET_FORMAT_SUBTYPE_COUNT = 0x1032,
|
||||
SFC_GET_FORMAT_SUBTYPE = 0x1033,
|
||||
|
||||
SFC_CALC_SIGNAL_MAX = 0x1040,
|
||||
SFC_CALC_NORM_SIGNAL_MAX = 0x1041,
|
||||
SFC_CALC_MAX_ALL_CHANNELS = 0x1042,
|
||||
SFC_CALC_NORM_MAX_ALL_CHANNELS = 0x1043,
|
||||
SFC_GET_SIGNAL_MAX = 0x1044,
|
||||
SFC_GET_MAX_ALL_CHANNELS = 0x1045,
|
||||
|
||||
SFC_SET_ADD_PEAK_CHUNK = 0x1050,
|
||||
|
||||
SFC_UPDATE_HEADER_NOW = 0x1060,
|
||||
SFC_SET_UPDATE_HEADER_AUTO = 0x1061,
|
||||
|
||||
SFC_FILE_TRUNCATE = 0x1080,
|
||||
|
||||
SFC_SET_RAW_START_OFFSET = 0x1090,
|
||||
|
||||
SFC_SET_DITHER_ON_WRITE = 0x10A0,
|
||||
SFC_SET_DITHER_ON_READ = 0x10A1,
|
||||
|
||||
SFC_GET_DITHER_INFO_COUNT = 0x10A2,
|
||||
SFC_GET_DITHER_INFO = 0x10A3,
|
||||
|
||||
SFC_GET_EMBED_FILE_INFO = 0x10B0,
|
||||
|
||||
SFC_SET_CLIPPING = 0x10C0,
|
||||
SFC_GET_CLIPPING = 0x10C1,
|
||||
|
||||
SFC_GET_INSTRUMENT = 0x10D0,
|
||||
SFC_SET_INSTRUMENT = 0x10D1,
|
||||
|
||||
SFC_GET_LOOP_INFO = 0x10E0,
|
||||
|
||||
SFC_GET_BROADCAST_INFO = 0x10F0,
|
||||
SFC_SET_BROADCAST_INFO = 0x10F1,
|
||||
|
||||
// Following commands for testing only.
|
||||
SFC_TEST_IEEE_FLOAT_REPLACE = 0x6001,
|
||||
|
||||
/+
|
||||
+ SFC_SET_ADD_* values are deprecated and will disappear at some
|
||||
+ time in the future. They are guaranteed to be here up to and
|
||||
+ including version 1.0.8 to avoid breakage of existng software.
|
||||
+ They currently do nothing and will continue to do nothing.
|
||||
+/
|
||||
SFC_SET_ADD_DITHER_ON_WRITE = 0x1070,
|
||||
SFC_SET_ADD_DITHER_ON_READ = 0x1071
|
||||
}
|
||||
|
||||
|
||||
/+
|
||||
+ String types that can be set and read from files. Not all file types
|
||||
+ support this and even the file types which support one, may not support
|
||||
+ all string types.
|
||||
+/
|
||||
|
||||
enum
|
||||
{
|
||||
SF_STR_TITLE = 0x01,
|
||||
SF_STR_COPYRIGHT = 0x02,
|
||||
SF_STR_SOFTWARE = 0x03,
|
||||
SF_STR_ARTIST = 0x04,
|
||||
SF_STR_COMMENT = 0x05,
|
||||
SF_STR_DATE = 0x06
|
||||
}
|
||||
|
||||
/+
|
||||
+ Use the following as the start and end index when doing metadata
|
||||
+ transcoding.
|
||||
+/
|
||||
|
||||
alias SF_STR_TITLE SF_STR_FIRST;
|
||||
alias SF_STR_DATE SF_STR_LAST;
|
||||
|
||||
enum
|
||||
{
|
||||
// True and false
|
||||
SF_FALSE = 0,
|
||||
SF_TRUE = 1,
|
||||
|
||||
// Modes for opening files.
|
||||
SFM_READ = 0x10,
|
||||
SFM_WRITE = 0x20,
|
||||
SFM_RDWR = 0x30
|
||||
}
|
||||
|
||||
/+
|
||||
+ Public error values. These are guaranteed to remain unchanged for the
|
||||
+ duration of the library major version number. There are also a large number
|
||||
+ of private error numbers which are internal to the library which can change
|
||||
+ at any time.
|
||||
+/
|
||||
|
||||
enum
|
||||
{
|
||||
SF_ERR_NO_ERROR = 0,
|
||||
SF_ERR_UNRECOGNISED_FORMAT = 1,
|
||||
SF_ERR_SYSTEM = 2,
|
||||
SF_ERR_MALFORMED_FILE = 3,
|
||||
SF_ERR_UNSUPPORTED_ENCODING = 4
|
||||
}
|
||||
|
||||
|
||||
// A SNDFILE* pointer can be passed around much like stdio.h's FILE* pointer.
|
||||
struct SNDFILE_tag {}
|
||||
alias SNDFILE_tag SNDFILE;
|
||||
|
||||
alias long sf_count_t;
|
||||
|
||||
enum : long
|
||||
{
|
||||
SF_COUNT_MAX = 0x7FFFFFFFFFFFFFFFL
|
||||
}
|
||||
|
||||
/+
|
||||
+ A pointer to a SF_INFO structure is passed to sf_open_read() and filled in.
|
||||
+ On write, the SF_INFO structure is filled in by the user and passed into
|
||||
+ sf_open_write().
|
||||
+/
|
||||
|
||||
struct SF_INFO
|
||||
{
|
||||
sf_count_t frames; /+ Used to be called samples.
|
||||
Changed to avoid confusion. +/
|
||||
int samplerate;
|
||||
int channels;
|
||||
int format;
|
||||
int sections;
|
||||
int seekable;
|
||||
}
|
||||
|
||||
/+
|
||||
+ The SF_FORMAT_INFO struct is used to retrieve information about the sound
|
||||
+ file formats libsndfile supports using the sf_command() interface.
|
||||
+
|
||||
+ Using this interface will allow applications to support new file formats
|
||||
+ and encoding types when libsndfile is upgraded, without requiring
|
||||
+ re-compilation of the application.
|
||||
+
|
||||
+ Please consult the libsndfile documentation (particularly the information
|
||||
+ on the sf_command() interface) for examples of its use.
|
||||
+/
|
||||
|
||||
struct SF_FORMAT_INFO
|
||||
{
|
||||
int format;
|
||||
char* name;
|
||||
char* extension;
|
||||
}
|
||||
|
||||
/+
|
||||
+ Enums and typedefs for adding dither on read and write.
|
||||
+ See the html documentation for sf_command(), SFC_SET_DITHER_ON_WRITE
|
||||
+ and SFC_SET_DITHER_ON_READ.
|
||||
+/
|
||||
|
||||
enum
|
||||
{
|
||||
SFD_DEFAULT_LEVEL = 0,
|
||||
SFD_CUSTOM_LEVEL = 0x40000000,
|
||||
|
||||
SFD_NO_DITHER = 500,
|
||||
SFD_WHITE = 501,
|
||||
SFD_TRIANGULAR_PDF = 502
|
||||
}
|
||||
|
||||
struct SF_DITHER_INFO
|
||||
{
|
||||
int type;
|
||||
double level;
|
||||
char* name;
|
||||
}
|
||||
|
||||
/+
|
||||
+ Struct used to retrieve information about a file embedded within a
|
||||
+ larger file. See SFC_GET_EMBED_FILE_INFO.
|
||||
+/
|
||||
|
||||
struct SF_EMBED_FILE_INFO
|
||||
{
|
||||
sf_count_t offset;
|
||||
sf_count_t length;
|
||||
}
|
||||
|
||||
|
||||
// Structs used to retrieve music sample information from a file.
|
||||
|
||||
enum
|
||||
{
|
||||
// The loop mode field in SF_INSTRUMENT will be one of the following.
|
||||
SF_LOOP_NONE = 800,
|
||||
SF_LOOP_FORWARD,
|
||||
SF_LOOP_BACKWARD,
|
||||
SF_LOOP_ALTERNATING
|
||||
}
|
||||
|
||||
struct SF_INSTRUMENT
|
||||
{
|
||||
int gain;
|
||||
byte basenote, detune;
|
||||
byte velocity_lo, velocity_hi;
|
||||
byte key_lo, key_hi;
|
||||
int loop_count;
|
||||
|
||||
struct __loops
|
||||
{ int mode;
|
||||
uint start;
|
||||
uint end;
|
||||
uint count;
|
||||
}
|
||||
|
||||
__loops[16] loops;
|
||||
}
|
||||
|
||||
|
||||
// Struct used to retrieve loop information from a file.
|
||||
struct SF_LOOP_INFO
|
||||
{
|
||||
short time_sig_num; // any positive integer > 0
|
||||
short time_sig_den; // any positive power of 2 > 0
|
||||
int loop_mode; // see SF_LOOP enum
|
||||
|
||||
int num_beats; /+ this is NOT the amount of quarter notes !!!
|
||||
a full bar of 4/4 is 4 beats
|
||||
a full bar of 7/8 is 7 beats +/
|
||||
|
||||
float bpm; /+ suggestion, as it can be calculated using other
|
||||
fields (file's length, file's sampleRate and
|
||||
our time_sig_den). bpms are always the amount
|
||||
of _quarter notes_ per minute +/
|
||||
|
||||
int root_key; // MIDI note, or -1 for None
|
||||
int[6] future;
|
||||
}
|
||||
|
||||
|
||||
/+
|
||||
+ Struct used to retrieve broadcast (EBU) information from a file.
|
||||
+ Strongly (!) based on EBU "bext" chunk format used in Broadcast WAVE.
|
||||
+/
|
||||
struct SF_BROADCAST_INFO
|
||||
{
|
||||
byte[256] description;
|
||||
byte[32] originator;
|
||||
byte[32] originator_reference;
|
||||
byte[10] origination_date;
|
||||
byte[8] origination_time;
|
||||
int time_reference_low;
|
||||
int time_reference_high;
|
||||
short version_; // XXX changed from version
|
||||
byte[64] umid;
|
||||
byte[190] reserved;
|
||||
uint coding_history_size ;
|
||||
byte[256] coding_history;
|
||||
}
|
||||
|
||||
|
||||
extern(C) alias sf_count_t function(void*) sf_vio_get_filelen;
|
||||
extern(C) alias sf_count_t function(sf_count_t, int, void*) sf_vio_seek;
|
||||
extern(C) alias sf_count_t function(void*, sf_count_t, void*) sf_vio_read;
|
||||
extern(C) alias sf_count_t function(void*, sf_count_t, void*) sf_vio_write;
|
||||
extern(C) alias sf_count_t function(void*) sf_vio_tell;
|
||||
|
||||
struct SF_VIRTUAL_IO
|
||||
{
|
||||
sf_vio_get_filelen get_filelen;
|
||||
sf_vio_seek seek;
|
||||
sf_vio_read read;
|
||||
sf_vio_write write;
|
||||
sf_vio_tell tell;
|
||||
}
|
||||
|
||||
private void loadsndfile(SharedLib lib)
|
||||
{
|
||||
bindFunc(sf_open)("sf_open", lib);
|
||||
bindFunc(sf_open_fd)("sf_open_fd", lib);
|
||||
bindFunc(sf_open_virtual)("sf_open_virtual", lib);
|
||||
bindFunc(sf_error)("sf_error", lib);
|
||||
bindFunc(sf_strerror)("sf_strerror", lib);
|
||||
bindFunc(sf_error_number)("sf_error_number", lib);
|
||||
bindFunc(sf_perror)("sf_perror", lib);
|
||||
bindFunc(sf_error_str)("sf_error_str", lib);
|
||||
bindFunc(sf_command)("sf_command", lib);
|
||||
bindFunc(sf_format_check)("sf_format_check", lib);
|
||||
bindFunc(sf_seek)("sf_seek", lib);
|
||||
bindFunc(sf_set_string)("sf_set_string", lib);
|
||||
bindFunc(sf_get_string)("sf_get_string", lib);
|
||||
bindFunc(sf_read_raw)("sf_read_raw", lib);
|
||||
bindFunc(sf_write_raw)("sf_write_raw", lib);
|
||||
bindFunc(sf_readf_short)("sf_readf_short", lib);
|
||||
bindFunc(sf_writef_short)("sf_writef_short", lib);
|
||||
bindFunc(sf_readf_int)("sf_readf_int", lib);
|
||||
bindFunc(sf_writef_int)("sf_writef_int", lib);
|
||||
bindFunc(sf_readf_float)("sf_readf_float", lib);
|
||||
bindFunc(sf_writef_float)("sf_writef_float", lib);
|
||||
bindFunc(sf_readf_double)("sf_readf_double", lib);
|
||||
bindFunc(sf_writef_double)("sf_writef_double", lib);
|
||||
bindFunc(sf_read_short)("sf_read_short", lib);
|
||||
bindFunc(sf_write_short)("sf_write_short", lib);
|
||||
bindFunc(sf_read_int)("sf_read_int", lib);
|
||||
bindFunc(sf_write_int)("sf_write_int", lib);
|
||||
bindFunc(sf_read_float)("sf_read_float", lib);
|
||||
bindFunc(sf_write_float)("sf_write_float", lib);
|
||||
bindFunc(sf_read_double)("sf_read_double", lib);
|
||||
bindFunc(sf_write_double)("sf_write_double", lib);
|
||||
bindFunc(sf_close)("sf_close", lib);
|
||||
bindFunc(sf_write_sync)("sf_write_sync", lib);
|
||||
}
|
||||
|
||||
GenericLoader libsndfile;
|
||||
static this()
|
||||
{
|
||||
libsndfile.setup(
|
||||
"libsndfile.dll",
|
||||
"libsndfile.so, libsndfile.so.1",
|
||||
"",
|
||||
&loadsndfile);
|
||||
}
|
||||
|
||||
extern(C):
|
||||
|
||||
/+
|
||||
+ Open the specified file for read, write or both. On error, this will
|
||||
+ return a NULL pointer. To find the error number, pass a NULL SNDFILE
|
||||
+ to sf_perror() or sf_error_str().
|
||||
+ All calls to sf_open() should be matched with a call to sf_close().
|
||||
+/
|
||||
|
||||
typedef SNDFILE* function(char* path, int mode, SF_INFO* sfinfo) pfsf_open;
|
||||
pfsf_open sf_open;
|
||||
|
||||
/+
|
||||
+ Use the existing file descriptor to create a SNDFILE object. If close_desc
|
||||
+ is TRUE, the file descriptor will be closed when sf_close() is called. If
|
||||
+ it is FALSE, the descritor will not be closed.
|
||||
+ When passed a descriptor like this, the library will assume that the start
|
||||
+ of file header is at the current file offset. This allows sound files within
|
||||
+ larger container files to be read and/or written.
|
||||
+ On error, this will return a NULL pointer. To find the error number, pass a
|
||||
+ NULL SNDFILE to sf_perror() or sf_error_str().
|
||||
+ All calls to sf_open_fd() should be matched with a call to sf_close().
|
||||
+/
|
||||
|
||||
typedef SNDFILE* function(int fd, int mode, SF_INFO* sfinfo, int close_desc)
|
||||
pfsf_open_fd;
|
||||
typedef SNDFILE* function(SF_VIRTUAL_IO* sfvirtual, int mode, SF_INFO* sfinfo,
|
||||
void* user_data) pfsf_open_virtual;
|
||||
|
||||
pfsf_open_fd sf_open_fd;
|
||||
pfsf_open_virtual sf_open_virtual;
|
||||
|
||||
/+
|
||||
+ sf_error() returns a error number which can be translated to a text
|
||||
+ string using sf_error_number().
|
||||
+/
|
||||
|
||||
typedef int function(SNDFILE* sndfile) pfsf_error;
|
||||
pfsf_error sf_error;
|
||||
|
||||
/+
|
||||
+ sf_strerror() returns to the caller a pointer to the current error message
|
||||
+ for the given SNDFILE.
|
||||
+/
|
||||
|
||||
typedef char* function(SNDFILE* sndfile) pfsf_strerror;
|
||||
pfsf_strerror sf_strerror;
|
||||
|
||||
/+
|
||||
+ sf_error_number() allows the retrieval of the error string for each
|
||||
+ internal error number.
|
||||
+/
|
||||
|
||||
typedef char* function(int errnum) pfsf_error_number;
|
||||
pfsf_error_number sf_error_number;
|
||||
|
||||
/+
|
||||
+ The following three error functions are deprecated but they will remain in
|
||||
+ the library for the forseeable future. The function sf_strerror() should be
|
||||
+ used in their place.
|
||||
+/
|
||||
|
||||
typedef int function(SNDFILE* sndfile) pfsf_perror;
|
||||
typedef int function(SNDFILE* sndfile, char* str, size_t len) pfsf_error_str;
|
||||
|
||||
pfsf_perror sf_perror;
|
||||
pfsf_error_str sf_error_str;
|
||||
|
||||
/+
|
||||
+ Return TRUE if fields of the SF_INFO struct are a valid combination of
|
||||
+ values.
|
||||
+/
|
||||
|
||||
typedef int function(SNDFILE* sndfile, int command, void* data, int datasize)
|
||||
pfsf_command;
|
||||
pfsf_command sf_command;
|
||||
|
||||
/+
|
||||
+ Return TRUE if fields of the SF_INFO struct are a valid combination of
|
||||
+ values.
|
||||
+/
|
||||
|
||||
typedef int function(SF_INFO* info) pfsf_format_check;
|
||||
pfsf_format_check sf_format_check;
|
||||
|
||||
/+
|
||||
+ Seek within the waveform data chunk of the SNDFILE. sf_seek() uses
|
||||
+ the same values for whence (SEEK_SET, SEEK_CUR and SEEK_END) as
|
||||
+ stdio.h function fseek().
|
||||
+ An offset of zero with whence set to SEEK_SET will position the
|
||||
+ read / write pointer to the first data sample.
|
||||
+ On success sf_seek returns the current position in (multi-channel)
|
||||
+ samples from the start of the file.
|
||||
+ Please see the libsndfile documentation for moving the read pointer
|
||||
+ separately from the write pointer on files open in mode SFM_RDWR.
|
||||
+ On error all of these functions return -1.
|
||||
+/
|
||||
|
||||
typedef sf_count_t function(SNDFILE* sndfile, sf_count_t frames, int whence)
|
||||
pfsf_seek;
|
||||
pfsf_seek sf_seek;
|
||||
|
||||
/+
|
||||
+ Functions for retrieving and setting string data within sound files.
|
||||
+ Not all file types support this features; AIFF and WAV do. For both
|
||||
+ functions, the str_type parameter must be one of the SF_STR_* values
|
||||
+ defined above.
|
||||
+ On error, sf_set_string() returns non-zero while sf_get_string()
|
||||
+ returns NULL.
|
||||
+/
|
||||
|
||||
typedef int function(SNDFILE* sndfile, int str_type, char* str)
|
||||
pfsf_set_string;
|
||||
typedef char* function(SNDFILE* sndfile, int str_type) pfsf_get_string;
|
||||
|
||||
pfsf_set_string sf_set_string;
|
||||
pfsf_get_string sf_get_string;
|
||||
|
||||
// Functions for reading/writing the waveform data of a sound file.
|
||||
|
||||
typedef sf_count_t function(SNDFILE* sndfile, void* ptr, sf_count_t bytes)
|
||||
pfsf_read_raw;
|
||||
typedef sf_count_t function(SNDFILE* sndfile, void* ptr, sf_count_t bytes)
|
||||
pfsf_write_raw;
|
||||
|
||||
pfsf_read_raw sf_read_raw;
|
||||
pfsf_write_raw sf_write_raw;
|
||||
|
||||
/+
|
||||
+ Functions for reading and writing the data chunk in terms of frames.
|
||||
+ The number of items actually read/written = frames * number of channels.
|
||||
+ sf_xxxx_raw read/writes the raw data bytes from/to the file
|
||||
+ sf_xxxx_short passes data in the native short format
|
||||
+ sf_xxxx_int passes data in the native int format
|
||||
+ sf_xxxx_float passes data in the native float format
|
||||
+ sf_xxxx_double passes data in the native double format
|
||||
+ All of these read/write function return number of frames read/written.
|
||||
+/
|
||||
|
||||
typedef sf_count_t function(SNDFILE* sndfile, short* ptr, sf_count_t frames)
|
||||
pfsf_readf_short;
|
||||
typedef sf_count_t function(SNDFILE* sndfile, short* ptr, sf_count_t frames)
|
||||
pfsf_writef_short;
|
||||
|
||||
typedef sf_count_t function(SNDFILE* sndfile, int* ptr, sf_count_t frames)
|
||||
pfsf_readf_int;
|
||||
typedef sf_count_t function(SNDFILE* sndfile, int* ptr, sf_count_t frames)
|
||||
pfsf_writef_int;
|
||||
|
||||
typedef sf_count_t function(SNDFILE* sndfile, float* ptr, sf_count_t frames)
|
||||
pfsf_readf_float;
|
||||
typedef sf_count_t function(SNDFILE* sndfile, float* ptr, sf_count_t frames)
|
||||
pfsf_writef_float;
|
||||
|
||||
typedef sf_count_t function(SNDFILE* sndfile, double* ptr, sf_count_t frames)
|
||||
pfsf_readf_double;
|
||||
typedef sf_count_t function(SNDFILE* sndfile, double* ptr, sf_count_t frames)
|
||||
pfsf_writef_double;
|
||||
|
||||
pfsf_readf_short sf_readf_short;
|
||||
pfsf_writef_short sf_writef_short;
|
||||
pfsf_readf_int sf_readf_int;
|
||||
pfsf_writef_int sf_writef_int;
|
||||
pfsf_readf_float sf_readf_float;
|
||||
pfsf_writef_float sf_writef_float;
|
||||
pfsf_readf_double sf_readf_double;
|
||||
pfsf_writef_double sf_writef_double;
|
||||
|
||||
/+
|
||||
+ Functions for reading and writing the data chunk in terms of items.
|
||||
+ Otherwise similar to above.
|
||||
+ All of these read/write function return number of items read/written.
|
||||
+/
|
||||
|
||||
typedef sf_count_t function(SNDFILE* sndfile, short* ptr, sf_count_t items)
|
||||
pfsf_read_short;
|
||||
typedef sf_count_t function(SNDFILE* sndfile, short* ptr, sf_count_t items)
|
||||
pfsf_write_short;
|
||||
|
||||
typedef sf_count_t function(SNDFILE* sndfile, int* ptr, sf_count_t items)
|
||||
pfsf_read_int;
|
||||
typedef sf_count_t function(SNDFILE* sndfile, int* ptr, sf_count_t items)
|
||||
pfsf_write_int;
|
||||
|
||||
typedef sf_count_t function(SNDFILE* sndfile, float* ptr, sf_count_t items)
|
||||
pfsf_read_float;
|
||||
typedef sf_count_t function(SNDFILE* sndfile, float* ptr, sf_count_t items)
|
||||
pfsf_write_float;
|
||||
|
||||
typedef sf_count_t function(SNDFILE* sndfile, double* ptr, sf_count_t items)
|
||||
pfsf_read_double;
|
||||
typedef sf_count_t function(SNDFILE* sndfile, double* ptr, sf_count_t items)
|
||||
pfsf_write_double;
|
||||
|
||||
pfsf_read_short sf_read_short;
|
||||
pfsf_write_short sf_write_short;
|
||||
pfsf_read_int sf_read_int;
|
||||
pfsf_write_int sf_write_int;
|
||||
pfsf_read_float sf_read_float;
|
||||
pfsf_write_float sf_write_float;
|
||||
pfsf_read_double sf_read_double;
|
||||
pfsf_write_double sf_write_double;
|
||||
|
||||
/+
|
||||
+ Close the SNDFILE and clean up all memory allocations associated with this
|
||||
+ file.
|
||||
+ Returns 0 on success, or an error number.
|
||||
+/
|
||||
|
||||
typedef int function(SNDFILE* sndfile) pfsf_close;
|
||||
pfsf_close sf_close;
|
||||
|
||||
/+
|
||||
+ If the file is opened SFM_WRITE or SFM_RDWR, call fsync() on the file
|
||||
+ to force the writing of data to disk. If the file is opened SFM_READ
|
||||
+ no action is taken.
|
||||
+/
|
||||
|
||||
typedef void function(SNDFILE* sndfile) pfsf_write_sync;
|
||||
pfsf_write_sync sf_write_sync;
|
||||
|
305
src/system/base.d
Normal file
305
src/system/base.d
Normal file
@ -0,0 +1,305 @@
|
||||
/+
|
||||
+ system/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 system.base;
|
||||
|
||||
import timer;
|
||||
import memory;
|
||||
import d6502.base;
|
||||
|
||||
import ui.sound;
|
||||
import ui.inputevents;
|
||||
|
||||
import system.io;
|
||||
import system.video;
|
||||
import iomem;
|
||||
import video.base;
|
||||
import system.peripheral;
|
||||
|
||||
class System
|
||||
{
|
||||
Timer timer;
|
||||
Timer.Cycle deviceCycle;
|
||||
AddressDecoder decoder;
|
||||
SoftSwitchPage switches;
|
||||
CpuBase cpu;
|
||||
IOMem ioMem;
|
||||
Peripherals peripherals;
|
||||
|
||||
class Memory
|
||||
{
|
||||
PrimaryMem mainRam;
|
||||
Rom mainRom;
|
||||
VideoPages vidPages;
|
||||
|
||||
this(ubyte[] romDump)
|
||||
{
|
||||
mainRam = new PrimaryMem(0x0000, 0xC000);
|
||||
mainRam.debugName = "RAM";
|
||||
mainRom = new Rom(0xD000, 0x3000,
|
||||
romDump[(length-12288)..length]);
|
||||
mainRom.debugName = "ROM";
|
||||
vidPages.lores1 = new SliceMem(0x0400, 0x0400, mainRam);
|
||||
vidPages.lores2 = new SliceMem(0x0800, 0x0400, mainRam);
|
||||
vidPages.hires1 = new SliceMem(0x2000, 0x2000, mainRam);
|
||||
vidPages.hires2 = new SliceMem(0x4000, 0x2000, mainRam);
|
||||
}
|
||||
|
||||
void reboot()
|
||||
{
|
||||
mainRam.reboot();
|
||||
vidPages.reboot();
|
||||
}
|
||||
}
|
||||
|
||||
Video video_;
|
||||
Memory memory_;
|
||||
IO io_;
|
||||
|
||||
abstract IO newIO();
|
||||
abstract CpuBase newCpu();
|
||||
abstract Video newVideo(ubyte[] vidRom);
|
||||
abstract IOMem newIOMem();
|
||||
abstract Peripherals newPeripherals();
|
||||
|
||||
this(ubyte[] romDump)
|
||||
{
|
||||
initTimer();
|
||||
initMemory(romDump);
|
||||
initCpu();
|
||||
initIO(null); // XXX where is vidRom passed in?
|
||||
decoder.nullRead = &video_.scanner.floatingBus;
|
||||
|
||||
peripherals = newPeripherals();
|
||||
peripherals.install(cpu, decoder, memory_.mainRom);
|
||||
ioMem.initialize(decoder, switches, timer, peripherals);
|
||||
|
||||
input.onReset = &reset;
|
||||
switches.setFloatingBus(&video_.scanner.floatingBus);
|
||||
}
|
||||
|
||||
void reboot()
|
||||
{
|
||||
cpu.reboot();
|
||||
deviceCycle.restart();
|
||||
memory_.reboot();
|
||||
ioMem.reboot();
|
||||
io_.reboot();
|
||||
peripherals.reboot();
|
||||
video_.reboot();
|
||||
}
|
||||
|
||||
void initTimer()
|
||||
{
|
||||
// XXX constants? variables?
|
||||
timer = new Timer(10_205, 1_020_484);
|
||||
deviceCycle =
|
||||
timer.startCycle(timer.primaryCounter.startLength * 2);
|
||||
}
|
||||
|
||||
void initMemory(ubyte[] romDump)
|
||||
{
|
||||
memory_ = new Memory(romDump);
|
||||
decoder = new AddressDecoder();
|
||||
switches = new SoftSwitchPage();
|
||||
decoder.installSwitches(switches);
|
||||
decoder.install(memory_.mainRam);
|
||||
decoder.install(memory_.mainRom);
|
||||
ioMem = newIOMem();
|
||||
}
|
||||
|
||||
void initCpu()
|
||||
{
|
||||
cpu = newCpu();
|
||||
debug(disassemble) cpu.memoryName = &decoder.memoryReadName;
|
||||
cpu.tick = &timer.tick;
|
||||
timer.onPrimaryStop(&primaryStop);
|
||||
cpu.memoryRead = &decoder.read;
|
||||
cpu.memoryWrite = &decoder.write;
|
||||
}
|
||||
|
||||
void initIO(ubyte[] vidRom)
|
||||
{
|
||||
io_ = newIO();
|
||||
video_ = newVideo(vidRom);
|
||||
}
|
||||
|
||||
bool primaryStop()
|
||||
{
|
||||
cpu.stop();
|
||||
return true;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
peripherals.reset();
|
||||
cpu.resetLow();
|
||||
}
|
||||
|
||||
uint checkpoint()
|
||||
{
|
||||
return timer.primaryCounter.currentLength;
|
||||
}
|
||||
|
||||
uint sinceCheckpoint(uint cp)
|
||||
{
|
||||
uint currentLength = timer.primaryCounter.currentLength;
|
||||
return ((currentLength == timer.primaryCounter.startLength) ?
|
||||
cp : (cp - currentLength));
|
||||
}
|
||||
|
||||
void execute()
|
||||
{
|
||||
cpu.run(true);
|
||||
|
||||
soundCard.process();
|
||||
soundCard.start();
|
||||
|
||||
video_.signal.update();
|
||||
deviceCycle.restart();
|
||||
// XXX peripherals get notification
|
||||
}
|
||||
}
|
||||
|
||||
class II : System
|
||||
{
|
||||
import d6502.nmosundoc : NmosUndoc;
|
||||
|
||||
int revision;
|
||||
|
||||
this(ubyte[] romDump)
|
||||
{
|
||||
// XXX FIXME XXX
|
||||
revision = int.max;
|
||||
super(romDump);
|
||||
}
|
||||
|
||||
CpuBase newCpu()
|
||||
{
|
||||
return new NmosUndoc();
|
||||
}
|
||||
|
||||
IO newIO()
|
||||
{
|
||||
return new IO_II(switches, timer, deviceCycle);
|
||||
}
|
||||
|
||||
Video newVideo(ubyte[] vidRom)
|
||||
{
|
||||
return new Video_II(switches, memory_.vidPages, timer, vidRom,
|
||||
&io_.kbd.peekLatch, decoder);
|
||||
}
|
||||
|
||||
IOMem newIOMem()
|
||||
{
|
||||
return new IOMem();
|
||||
}
|
||||
|
||||
Peripherals newPeripherals()
|
||||
{
|
||||
return new Peripherals_II();
|
||||
}
|
||||
}
|
||||
|
||||
import ioummu;
|
||||
|
||||
class IIe : System
|
||||
{
|
||||
import d6502.cmos : Cmos;
|
||||
|
||||
AuxiliaryCard auxCard;
|
||||
MMU mmu;
|
||||
IOU iou;
|
||||
|
||||
this(ubyte[] romDump)
|
||||
{
|
||||
// XXX if different or no aux card?
|
||||
//auxMemory = new Memory();
|
||||
super(romDump);
|
||||
}
|
||||
|
||||
void reboot()
|
||||
{
|
||||
super.reboot();
|
||||
auxCard.reboot();
|
||||
mmu.reboot();
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
auxCard.reset();
|
||||
mmu.reset();
|
||||
super.reset();
|
||||
}
|
||||
|
||||
CpuBase newCpu()
|
||||
{
|
||||
// XXX this is enhanced
|
||||
return new Cmos();
|
||||
}
|
||||
|
||||
IO newIO()
|
||||
{
|
||||
return new IO_IIe(switches, timer, deviceCycle);
|
||||
}
|
||||
|
||||
Video newVideo(ubyte[] vidRom)
|
||||
{
|
||||
return new Video_IIe(switches, memory_.vidPages, timer, vidRom,
|
||||
&io_.kbd.peekLatch, auxCard.vidPages);
|
||||
}
|
||||
|
||||
IOMem newIOMem()
|
||||
{
|
||||
return mmu.ioMem;
|
||||
}
|
||||
|
||||
Peripherals newPeripherals()
|
||||
{
|
||||
return new Peripherals_IIe();
|
||||
}
|
||||
|
||||
void initMemory(ubyte[] romDump)
|
||||
{
|
||||
mmu = new MMU();
|
||||
mmu.ioMem = new IOMem_IIe();
|
||||
mmu.ioMem.setRom(romDump);
|
||||
|
||||
super.initMemory(romDump);
|
||||
|
||||
// XXX XXX XXX
|
||||
// allow for different card from config
|
||||
auxCard = new Extended80ColumnCard();
|
||||
|
||||
mmu.init(switches, auxCard, decoder, memory_.mainRam,
|
||||
memory_.mainRom);
|
||||
}
|
||||
|
||||
void initIO(ubyte[] vidRom)
|
||||
{
|
||||
super.initIO(vidRom);
|
||||
iou = new IOU(io_, video_.signal);
|
||||
iou.initSwitches(switches);
|
||||
mmu.initIO(video_.scanner, &io_.kbd.peekLatch);
|
||||
}
|
||||
}
|
||||
|
158
src/system/io.d
Normal file
158
src/system/io.d
Normal file
@ -0,0 +1,158 @@
|
||||
/+
|
||||
+ system/io.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 system.io;
|
||||
|
||||
import memory;
|
||||
import timer;
|
||||
|
||||
import ui.inputevents;
|
||||
import ui.sound;
|
||||
|
||||
import device.annunciator;
|
||||
import device.pushbutton;
|
||||
import device.speaker;
|
||||
import device.keyboard;
|
||||
import device.paddle;
|
||||
|
||||
class IO
|
||||
{
|
||||
Annunciator annun;
|
||||
Paddles paddles;
|
||||
Pushbuttons buttons;
|
||||
Speaker spkr;
|
||||
Keyboard kbd;
|
||||
|
||||
this(SoftSwitchPage switches, Timer timer, Timer.Cycle deviceCycle)
|
||||
{
|
||||
makeSpeaker(switches, timer, deviceCycle);
|
||||
makeButtons(switches);
|
||||
makeKeyboard(switches);
|
||||
makePaddles(switches, timer);
|
||||
makeAnnunciators(switches);
|
||||
}
|
||||
|
||||
void makeSpeaker(SoftSwitchPage switches, Timer timer,
|
||||
Timer.Cycle deviceCycle)
|
||||
{
|
||||
if (soundCard.isActive)
|
||||
{
|
||||
spkr = new Speaker();
|
||||
spkr.initSwitches(switches);
|
||||
// XXX reference to SoundCardYes is bad?
|
||||
spkr.setTiming(timer.hertz, SoundCardYes.sampleFreq, deviceCycle);
|
||||
soundCard.installSpeaker(spkr);
|
||||
}
|
||||
}
|
||||
|
||||
void makeButtons(SoftSwitchPage switches)
|
||||
{
|
||||
buttons = new Pushbuttons();
|
||||
buttons.initSwitches(switches);
|
||||
input.installButtons(buttons);
|
||||
}
|
||||
|
||||
void makeKeyboard(SoftSwitchPage switches)
|
||||
{
|
||||
kbd = newKeyboard();
|
||||
kbd.initSwitches(switches);
|
||||
kbd.buttons = buttons;
|
||||
input.installKeyboard(kbd);
|
||||
}
|
||||
|
||||
void makePaddles(SoftSwitchPage switches, Timer timer)
|
||||
{
|
||||
paddles = newPaddles();
|
||||
paddles.initSwitches(switches);
|
||||
paddles.timer = timer;
|
||||
input.installPaddles(paddles);
|
||||
}
|
||||
|
||||
void makeAnnunciators(SoftSwitchPage switches)
|
||||
{
|
||||
annun = newAnnunciators();
|
||||
annun.initSwitches(switches);
|
||||
}
|
||||
|
||||
void reboot()
|
||||
{
|
||||
annun.reboot();
|
||||
buttons.reboot();
|
||||
kbd.reboot();
|
||||
paddles.reboot();
|
||||
}
|
||||
|
||||
abstract Keyboard newKeyboard();
|
||||
abstract Annunciator newAnnunciators();
|
||||
abstract Paddles newPaddles();
|
||||
}
|
||||
|
||||
class IO_II : IO
|
||||
{
|
||||
this(SoftSwitchPage switches, Timer timer, Timer.Cycle deviceCycle)
|
||||
{
|
||||
super(switches, timer, deviceCycle);
|
||||
}
|
||||
|
||||
Keyboard newKeyboard()
|
||||
{
|
||||
return new Keyboard_II();
|
||||
}
|
||||
|
||||
Annunciator newAnnunciators()
|
||||
{
|
||||
return new Annunciator_II();
|
||||
}
|
||||
|
||||
Paddles newPaddles()
|
||||
{
|
||||
return new Paddles_II();
|
||||
}
|
||||
}
|
||||
|
||||
class IO_IIe : IO
|
||||
{
|
||||
this(SoftSwitchPage switches, Timer timer, Timer.Cycle deviceCycle)
|
||||
{
|
||||
super(switches, timer, deviceCycle);
|
||||
}
|
||||
|
||||
Keyboard newKeyboard()
|
||||
{
|
||||
return new Keyboard_IIe();
|
||||
}
|
||||
|
||||
Annunciator newAnnunciators()
|
||||
{
|
||||
// NOTE: C058-C05F are handled by IOU
|
||||
return new Annunciator();
|
||||
}
|
||||
|
||||
Paddles newPaddles()
|
||||
{
|
||||
// XXX if unenhanced:
|
||||
//return new Paddles_II();
|
||||
// XXX else:
|
||||
return new Paddles();
|
||||
}
|
||||
}
|
||||
|
87
src/system/peripheral.d
Normal file
87
src/system/peripheral.d
Normal file
@ -0,0 +1,87 @@
|
||||
/+
|
||||
+ system/peripheral.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 system.peripheral;
|
||||
|
||||
import memory;
|
||||
import d6502.base;
|
||||
|
||||
import peripheral.base;
|
||||
import peripheral.diskii;
|
||||
import peripheral.langcard;
|
||||
import peripheral.saturn128;
|
||||
|
||||
class Peripherals
|
||||
{
|
||||
Peripheral[8] cards;
|
||||
|
||||
abstract void install(CpuBase cpu, AddressDecoder decoder, Rom mainRom);
|
||||
|
||||
void reboot()
|
||||
{
|
||||
for (int p = 0; p < 8; ++p)
|
||||
{
|
||||
if (cards[p] !is null)
|
||||
cards[p].reboot();
|
||||
}
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
for (int s = 0; s < 8; ++s)
|
||||
{
|
||||
if (cards[s] !is null)
|
||||
cards[s].reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Peripherals_II : Peripherals
|
||||
{
|
||||
void install(CpuBase cpu, AddressDecoder decoder, Rom mainRom)
|
||||
{
|
||||
auto diskController = new Controller();
|
||||
diskController.checkFinalCycle = &cpu.checkFinalCycle;
|
||||
cards[6] = diskController; // XXX
|
||||
|
||||
auto langCard = new LanguageCard();
|
||||
langCard.implementation.init(decoder, &mainRom.read, &mainRom.write);
|
||||
cards[0] = langCard;
|
||||
|
||||
/+
|
||||
auto saturn = new Saturn128();
|
||||
saturn.init(decoder, &mainRom.read, &mainRom.write);
|
||||
cards[0] = saturn;
|
||||
+/
|
||||
}
|
||||
}
|
||||
|
||||
class Peripherals_IIe : Peripherals
|
||||
{
|
||||
void install(CpuBase cpu, AddressDecoder decoder, Rom mainRom)
|
||||
{
|
||||
auto diskController = new Controller();
|
||||
diskController.checkFinalCycle = &cpu.checkFinalCycle;
|
||||
cards[6] = diskController; // XXX
|
||||
}
|
||||
}
|
||||
|
124
src/system/video.d
Normal file
124
src/system/video.d
Normal file
@ -0,0 +1,124 @@
|
||||
/+
|
||||
+ system/video.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 system.video;
|
||||
|
||||
import memory;
|
||||
import timer;
|
||||
|
||||
import ui.monitor;
|
||||
|
||||
import video.base;
|
||||
import video.scanner;
|
||||
import video.signal;
|
||||
|
||||
class Video
|
||||
{
|
||||
Scanner scanner;
|
||||
Signal signal;
|
||||
Screen screen;
|
||||
|
||||
this(SoftSwitchPage switches, VideoPages vidPages, Timer timer,
|
||||
ubyte[] vidRom, ubyte delegate() peekLatch)
|
||||
{
|
||||
signal = newSignal();
|
||||
scanner = newScanner();
|
||||
|
||||
screen = monitor.screen;
|
||||
screen.scanner = scanner;
|
||||
|
||||
scanner.signal = signal;
|
||||
scanner.drawFrame = &monitor.glDrawFrame;
|
||||
scanner.kbdLatch = peekLatch;
|
||||
scanner.init(timer);
|
||||
scanner.installMemory(vidPages.lores1, vidPages.lores2,
|
||||
vidPages.hires1, vidPages.hires2);
|
||||
scanner.initSwitches(switches);
|
||||
|
||||
signal.scanner = scanner;
|
||||
signal.screen = screen;
|
||||
signal.kbdLatch = peekLatch;
|
||||
signal.init(timer, vidRom);
|
||||
signal.initSwitches(switches);
|
||||
}
|
||||
|
||||
void reboot()
|
||||
{
|
||||
signal.reboot();
|
||||
scanner.reboot();
|
||||
}
|
||||
|
||||
void forceFrame()
|
||||
{
|
||||
scanner.forceFrame();
|
||||
monitor.glDrawFrame();
|
||||
}
|
||||
|
||||
abstract Scanner newScanner();
|
||||
abstract Signal newSignal();
|
||||
}
|
||||
|
||||
class Video_II : Video
|
||||
{
|
||||
Scanner_II scanner40;
|
||||
|
||||
this(SoftSwitchPage switches, VideoPages vidPages, Timer timer,
|
||||
ubyte[] vidRom, ubyte delegate() peekLatch,
|
||||
AddressDecoder decoder)
|
||||
{
|
||||
super(switches, vidPages, timer, vidRom, peekLatch);
|
||||
scanner40.decoder = decoder;
|
||||
}
|
||||
|
||||
Scanner newScanner()
|
||||
{
|
||||
scanner40 = new Scanner_II();
|
||||
return scanner40;
|
||||
}
|
||||
|
||||
Signal newSignal()
|
||||
{
|
||||
return new Signal_II();
|
||||
}
|
||||
}
|
||||
|
||||
class Video_IIe : Video
|
||||
{
|
||||
this(SoftSwitchPage switches, VideoPages vidPages, Timer timer,
|
||||
ubyte[] vidRom, ubyte delegate() peekLatch, VideoPages auxPages)
|
||||
{
|
||||
super(switches, vidPages, timer, vidRom, peekLatch);
|
||||
scanner.installAuxMemory(auxPages.lores1, auxPages.lores2,
|
||||
auxPages.hires1, auxPages.hires2);
|
||||
}
|
||||
|
||||
Scanner newScanner()
|
||||
{
|
||||
return new Scanner_IIe();
|
||||
}
|
||||
|
||||
Signal newSignal()
|
||||
{
|
||||
return new Signal_IIe();
|
||||
}
|
||||
}
|
||||
|
244
src/timer.d
Normal file
244
src/timer.d
Normal file
@ -0,0 +1,244 @@
|
||||
/+
|
||||
+ timer.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 timer;
|
||||
|
||||
class Timer
|
||||
{
|
||||
class Cycle
|
||||
{
|
||||
int delta;
|
||||
uint rollOver;
|
||||
|
||||
this(uint maxVal)
|
||||
{
|
||||
rollOver = maxVal;
|
||||
restart();
|
||||
}
|
||||
|
||||
void restart()
|
||||
{
|
||||
delta = 0 - currentCounter.elapsed();
|
||||
}
|
||||
|
||||
uint currentVal()
|
||||
{
|
||||
return (currentCounter.elapsed() + delta) % rollOver;
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
delta = currentVal();
|
||||
}
|
||||
}
|
||||
|
||||
class Counter
|
||||
{
|
||||
bool delegate() expiry;
|
||||
uint startLength, currentLength;
|
||||
int ticks;
|
||||
bool shouldContinue;
|
||||
|
||||
this(uint start)
|
||||
{
|
||||
shouldContinue = true;
|
||||
startLength = currentLength = ticks = start;
|
||||
addCounter(this);
|
||||
}
|
||||
|
||||
this(uint start, bool delegate() expiration)
|
||||
{
|
||||
this(start);
|
||||
initCounter(this);
|
||||
expiry = expiration;
|
||||
}
|
||||
|
||||
final uint elapsed()
|
||||
{
|
||||
return currentLength - ticks;
|
||||
}
|
||||
|
||||
final void tick()
|
||||
{
|
||||
--ticks;
|
||||
if (ticks == 0)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
final void forceExpire()
|
||||
{
|
||||
ticks = 1;
|
||||
tick();
|
||||
}
|
||||
|
||||
final void discard()
|
||||
{
|
||||
expiry = &nullExpiry;
|
||||
forceExpire();
|
||||
}
|
||||
|
||||
private final void resume()
|
||||
{
|
||||
currentLength = ticks;
|
||||
}
|
||||
|
||||
private final bool expire()
|
||||
{
|
||||
ticks = currentLength = startLength;
|
||||
return expiry();
|
||||
}
|
||||
|
||||
private bool nullExpiry() { return false; }
|
||||
}
|
||||
|
||||
class DelayedCounter : Counter
|
||||
{
|
||||
uint realStart;
|
||||
bool delegate() realExpiry;
|
||||
|
||||
this(uint start, bool delegate() expiration, uint delay)
|
||||
{
|
||||
realStart = start;
|
||||
realExpiry = expiration;
|
||||
super(delay, &becomeReal);
|
||||
}
|
||||
|
||||
private bool becomeReal()
|
||||
{
|
||||
ticks = currentLength = startLength = realStart;
|
||||
expiry = realExpiry;
|
||||
bool retval = expiry();
|
||||
initCounter(this);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
Cycle[] cycles;
|
||||
Counter[] counters;
|
||||
Counter primaryCounter, currentCounter;
|
||||
uint hertz;
|
||||
|
||||
this(uint primaryStart, uint hz)
|
||||
{
|
||||
hertz = hz;
|
||||
cycles.length = 10;
|
||||
counters.length = 10;
|
||||
cycles.length = 0;
|
||||
counters.length = 0;
|
||||
currentCounter = primaryCounter = new Counter(primaryStart);
|
||||
}
|
||||
|
||||
final void onPrimaryStop(bool delegate() expiration)
|
||||
{
|
||||
primaryCounter.expiry = expiration;
|
||||
}
|
||||
|
||||
Cycle startCycle(uint maxVal)
|
||||
{
|
||||
cycles.length = cycles.length + 1;
|
||||
cycles[length-1] = new Cycle(maxVal);
|
||||
return cycles[length-1];
|
||||
}
|
||||
|
||||
void tick()
|
||||
{
|
||||
currentCounter.tick();
|
||||
}
|
||||
|
||||
private void deleteCounters()
|
||||
{
|
||||
int numCounters = counters.length;
|
||||
int lastCounter;
|
||||
main: for (int counter = 0; counter < counters.length; ++counter)
|
||||
{
|
||||
lastCounter = counter;
|
||||
while (!counters[counter].shouldContinue)
|
||||
{
|
||||
numCounters--;
|
||||
if (++counter >= counters.length) break main;
|
||||
}
|
||||
currentCounter = counters[lastCounter] = counters[counter];
|
||||
}
|
||||
if (numCounters < counters.length)
|
||||
{
|
||||
counters.length = numCounters;
|
||||
}
|
||||
}
|
||||
|
||||
private void addCounter(Counter newCounter)
|
||||
{
|
||||
counters.length = counters.length + 1;
|
||||
counters[length-1] = newCounter;
|
||||
}
|
||||
|
||||
private void initCounter(Counter newCounter)
|
||||
{
|
||||
if (newCounter.ticks < currentCounter.ticks)
|
||||
{
|
||||
reset(newCounter);
|
||||
}
|
||||
else
|
||||
{
|
||||
newCounter.ticks += currentCounter.elapsed();
|
||||
}
|
||||
}
|
||||
|
||||
private void reset(Counter newCounter = null)
|
||||
{
|
||||
// update cycle counts
|
||||
for (int cycle = 0; cycle < cycles.length; ++cycle)
|
||||
{
|
||||
cycles[cycle].update();
|
||||
}
|
||||
|
||||
// update counter counts
|
||||
for (int counter = 0; counter < counters.length; ++counter)
|
||||
{
|
||||
if (counters[counter] !is currentCounter &&
|
||||
counters[counter] !is newCounter)
|
||||
counters[counter].ticks -= currentCounter.elapsed();
|
||||
}
|
||||
|
||||
// check for expired counters
|
||||
for (int counter = 0; counter < counters.length; ++counter)
|
||||
{
|
||||
if (counters[counter].ticks <= 0)
|
||||
counters[counter].shouldContinue = counters[counter].expire();
|
||||
else
|
||||
counters[counter].resume();
|
||||
}
|
||||
|
||||
//delete counters that should be deleted
|
||||
deleteCounters();
|
||||
|
||||
// set current counter
|
||||
for (int counter = 0; counter < counters.length; ++counter)
|
||||
{
|
||||
if (counters[counter].ticks < currentCounter.ticks)
|
||||
currentCounter = counters[counter];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
115
src/twoapple.d
Normal file
115
src/twoapple.d
Normal file
@ -0,0 +1,115 @@
|
||||
//@ //////////////////////////////////////////////////////////////////////////
|
||||
//@ 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 std.stdio;
|
||||
|
||||
import d6502.base;
|
||||
import timer;
|
||||
import memory;
|
||||
import system.base;
|
||||
import ui.monitor;
|
||||
import ui.sound;
|
||||
import ui.inputevents;
|
||||
|
||||
import gthread.Thread;
|
||||
import gtk.GtkD;
|
||||
import glgdk.GLdInit;
|
||||
import gtkc.gdktypes;
|
||||
import gtkc.gtktypes;
|
||||
|
||||
import peripheral.base;
|
||||
import peripheral.diskii;
|
||||
import peripheral.langcard;
|
||||
import peripheral.saturn128;
|
||||
|
||||
class TestSystem : II
|
||||
{
|
||||
this(ubyte[] romDump)
|
||||
{
|
||||
super(romDump);
|
||||
}
|
||||
|
||||
void setRom(ubyte[] rom_data)
|
||||
{
|
||||
uint rom_len = rom_data.length;
|
||||
memory_.mainRom.data_[0..12288] = rom_data[(rom_len - 12288)..rom_len];
|
||||
}
|
||||
}
|
||||
|
||||
import std.file;
|
||||
import std.string;
|
||||
import device.speaker;
|
||||
import ui.mainwindow;
|
||||
import video.laz_engine;
|
||||
|
||||
// XXX Only needed for initializing peripheralStatus
|
||||
import gtk.VBox;
|
||||
import peripheral.base;
|
||||
|
||||
void main(string[] args)
|
||||
{
|
||||
// Init GTK
|
||||
Thread.init(null);
|
||||
GtkD.init(args);
|
||||
GLdInit.init(null, null);
|
||||
|
||||
// open config
|
||||
|
||||
input = new Input();
|
||||
monitor = new Monitor();
|
||||
auto screen = new LazEngine();
|
||||
monitor.installScreen(screen);
|
||||
|
||||
// XXX should make it so this can happen after system?
|
||||
peripheralStatus = new VBox(false, 3);
|
||||
|
||||
appWindow = new TwoappleMainWindow();
|
||||
|
||||
// Get ROM file
|
||||
TwoappleFile romFile = TwoappleFilePicker.open("ROM file", &checkRomFile);
|
||||
if (romFile is null) return;
|
||||
|
||||
System sys;
|
||||
if ((args.length > 1) && (args[1] == "--iie"))
|
||||
sys = new IIe(cast(ubyte[])std.file.read(romFile.fileName));
|
||||
else
|
||||
sys = new II(cast(ubyte[])std.file.read(romFile.fileName));
|
||||
appWindow.initSystem(sys);
|
||||
// XXX hack
|
||||
appWindow.configChanged = true;
|
||||
debug(disassemble)
|
||||
{
|
||||
input.pauseButton.setActive(true);
|
||||
}
|
||||
appWindow.mainLoop();
|
||||
|
||||
// save config
|
||||
}
|
||||
|
||||
string checkRomFile(TwoappleFile checkFile)
|
||||
{
|
||||
ulong fsize = checkFile.fileSize();
|
||||
// XXX allow 12288 for II/II+ ?
|
||||
if (fsize >= 20480 && fsize <= 32768)
|
||||
return null;
|
||||
else
|
||||
return "Invalid ROM file";
|
||||
}
|
||||
|
281
src/ui/inputevents.d
Normal file
281
src/ui/inputevents.d
Normal file
@ -0,0 +1,281 @@
|
||||
/+
|
||||
+ ui/inputvents.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 ui.inputevents;
|
||||
|
||||
import std.stdio;
|
||||
|
||||
import gtkglc.glgdktypes;
|
||||
import gtk.Widget;
|
||||
import gtkc.gtktypes;
|
||||
import gtk.Image;
|
||||
import gtk.ToggleToolButton;
|
||||
import gtk.GtkD;
|
||||
import gtk.Timeout;
|
||||
|
||||
import derelict.sdl.sdl;
|
||||
|
||||
import device.keyboard;
|
||||
import device.paddle;
|
||||
import device.pushbutton;
|
||||
|
||||
import ui.textinput;
|
||||
|
||||
static import host;
|
||||
|
||||
static this()
|
||||
{
|
||||
if (host.SDL)
|
||||
{
|
||||
if (SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) == -1)
|
||||
{
|
||||
writefln("%s", std.string.toString(SDL_GetError()));
|
||||
return;
|
||||
}
|
||||
|
||||
int numJoysticks = SDL_NumJoysticks();
|
||||
if (numJoysticks == 0) return;
|
||||
|
||||
joysticks.length = numJoysticks;
|
||||
SDL_JoystickEventState(SDL_ENABLE);
|
||||
|
||||
SDL_Joystick* device;
|
||||
for (int i = 0; i < numJoysticks; ++i)
|
||||
{
|
||||
device = SDL_JoystickOpen(i);
|
||||
if (device is null)
|
||||
{
|
||||
writefln("%s", std.string.toString(SDL_GetError()));
|
||||
continue;
|
||||
}
|
||||
writefln("Joystick %s: %s", i,
|
||||
std.string.toString(SDL_JoystickName(i)));
|
||||
|
||||
joysticks[i] = new Joystick(device,
|
||||
SDL_JoystickNumAxes(device),
|
||||
SDL_JoystickNumButtons(device));
|
||||
}
|
||||
if (device is null)
|
||||
joysticks.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Input input;
|
||||
|
||||
private:
|
||||
|
||||
Joystick[] joysticks;
|
||||
|
||||
class Joystick
|
||||
{
|
||||
bool isActive;
|
||||
|
||||
int deviceNum;
|
||||
SDL_Joystick* device;
|
||||
|
||||
int[] pdlForAxis;
|
||||
int[] pbForButton;
|
||||
|
||||
this(SDL_Joystick* dev, int numAxes, int numButtons)
|
||||
{
|
||||
device = dev;
|
||||
pdlForAxis.length = numAxes;
|
||||
pbForButton.length = numButtons;
|
||||
for (int i = 0; i < numAxes; ++i)
|
||||
pdlForAxis[i] = -1;
|
||||
for (int i = 0; i < numButtons; ++i)
|
||||
pbForButton[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
class Input
|
||||
{
|
||||
SDL_Event sEvent;
|
||||
Timeout timeout;
|
||||
|
||||
Keyboard kbd;
|
||||
Pushbuttons buttons;
|
||||
Paddles paddles;
|
||||
TextInput textIn;
|
||||
ToggleToolButton pauseButton;
|
||||
void delegate() onReset;
|
||||
|
||||
this()
|
||||
{
|
||||
pauseButton = new ToggleToolButton("gtk-media-pause");
|
||||
// XXX read from config / allow changing
|
||||
for (int i = 0; i < joysticks.length; ++i)
|
||||
{
|
||||
// By default, ignore all but the first two joysticks
|
||||
if (i > 1) break;
|
||||
|
||||
joysticks[i].pdlForAxis[0] = i * 2;
|
||||
joysticks[i].pdlForAxis[1] = i * 2 + 1;
|
||||
joysticks[i].pbForButton[0] = i * 2;
|
||||
joysticks[i].pbForButton[1] = 1;
|
||||
|
||||
// XXX hackish way of setting "pause" button
|
||||
if (joysticks[i].pbForButton.length > 2)
|
||||
joysticks[i].pbForButton[length - 1] = -2;
|
||||
}
|
||||
}
|
||||
|
||||
void installButtons(Pushbuttons btns)
|
||||
{
|
||||
buttons = btns;
|
||||
}
|
||||
|
||||
void installKeyboard(Keyboard keyboard)
|
||||
{
|
||||
kbd = keyboard;
|
||||
}
|
||||
|
||||
void installPaddles(Paddles pdl)
|
||||
{
|
||||
paddles = pdl;
|
||||
}
|
||||
|
||||
gboolean onKeyPress(GdkEventKey* evt, Widget w)
|
||||
{
|
||||
static const int KEY_PAUSE = 65299; // Pause/Break
|
||||
static const int KEY_ESCAPE = 65307;
|
||||
|
||||
// Let someone else handle ALT-<x> presses
|
||||
if ((evt.state & GdkModifierType.MOD1_MASK) != 0) return false;
|
||||
|
||||
if (textIn !is null)
|
||||
{
|
||||
if (evt.keyval == KEY_ESCAPE)
|
||||
{
|
||||
stopTextInput();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
if (evt.keyval == KEY_PAUSE)
|
||||
{
|
||||
pauseButton.setActive(!(pauseButton.getActive()));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pauseButton.getActive()) return false;
|
||||
|
||||
bool ctrl = ((evt.state & GdkModifierType.CONTROL_MASK) != 0);
|
||||
return kbd.handlePress(evt.keyval, ctrl, evt.hardwareKeycode);
|
||||
}
|
||||
|
||||
gboolean onKeyRelease(GdkEventKey* evt, Widget w)
|
||||
{
|
||||
static const int KEY_RESET = 65481; // F12
|
||||
|
||||
// Let someone else handle ALT-<x> releases
|
||||
if ((evt.state & GdkModifierType.MOD1_MASK) != 0) return false;
|
||||
|
||||
if (evt.keyval == KEY_RESET)
|
||||
{
|
||||
onReset();
|
||||
if (pauseButton.getActive())
|
||||
{
|
||||
pauseButton.setActive(false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pauseButton.getActive()) return false;
|
||||
|
||||
bool ctrl = ((evt.state & GdkModifierType.CONTROL_MASK) != 0);
|
||||
return kbd.handleRelease(evt.keyval, ctrl, evt.hardwareKeycode);
|
||||
}
|
||||
|
||||
void onJoystickEvent()
|
||||
{
|
||||
static const float scale = 65535.0 / 2760.0;
|
||||
static const int shift = 32768;
|
||||
bool buttonDown = false;
|
||||
switch(sEvent.type)
|
||||
{
|
||||
case SDL_JOYAXISMOTION:
|
||||
Joystick joy = joysticks[sEvent.jaxis.which];
|
||||
int axis = sEvent.jaxis.axis;
|
||||
int pdl = joy.pdlForAxis[axis];
|
||||
if (pdl != -1)
|
||||
{
|
||||
int value =
|
||||
cast(int)(((sEvent.jaxis.value) + shift) / scale);
|
||||
paddles.update(pdl, value);
|
||||
}
|
||||
break;
|
||||
case SDL_JOYBUTTONDOWN:
|
||||
buttonDown = true;
|
||||
case SDL_JOYBUTTONUP:
|
||||
Joystick joy = joysticks[sEvent.jbutton.which];
|
||||
int pb = joy.pbForButton[sEvent.jbutton.button];
|
||||
if ((pb == -2) && buttonDown)
|
||||
pauseButton.setActive(!pauseButton.getActive());
|
||||
else if (pb >= 0)
|
||||
{
|
||||
buttons.update(pb, buttonDown);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void startTextInput(string filename)
|
||||
{
|
||||
textIn = new TextInput(filename, &stopTextInput);
|
||||
kbd.onReadLatch = &textIn.read;
|
||||
kbd.onClearStrobe = &textIn.advancePos;
|
||||
}
|
||||
|
||||
void stopTextInput()
|
||||
{
|
||||
kbd.onReadLatch = null;
|
||||
kbd.onClearStrobe = null;
|
||||
delete textIn;
|
||||
}
|
||||
|
||||
void processEvents()
|
||||
{
|
||||
kbd.processPresses();
|
||||
kbd.processReleases();
|
||||
|
||||
while(GtkD.eventsPending())
|
||||
{
|
||||
GtkD.mainIteration();
|
||||
}
|
||||
processJoystickEvents();
|
||||
}
|
||||
|
||||
bool processJoystickEvents()
|
||||
{
|
||||
while (SDL_PollEvent(&sEvent))
|
||||
{
|
||||
onJoystickEvent();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
454
src/ui/mainwindow.d
Normal file
454
src/ui/mainwindow.d
Normal file
@ -0,0 +1,454 @@
|
||||
/+
|
||||
+ ui/mainwindow.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 ui.mainwindow;
|
||||
|
||||
import std.string;
|
||||
|
||||
import gtk.MainWindow;
|
||||
import gtkc.gtktypes;
|
||||
|
||||
import ui.inputevents;
|
||||
import ui.sound;
|
||||
import ui.monitor;
|
||||
import host;
|
||||
import system.base;
|
||||
|
||||
TwoappleMainWindow appWindow;
|
||||
|
||||
class TwoappleMainWindow : MainWindow
|
||||
{
|
||||
import gtk.MenuBar;
|
||||
import gtk.Widget;
|
||||
import gdk.Event;
|
||||
import gtk.Toolbar;
|
||||
import gtk.ToggleToolButton;
|
||||
import gtk.SeparatorToolItem;
|
||||
import gtk.ToolButton;
|
||||
import gtk.SeparatorToolItem;
|
||||
import gtk.MenuItem;
|
||||
import gtk.AccelGroup;
|
||||
import gtk.VBox;
|
||||
import gtk.Toolbar;
|
||||
import gtk.Label;
|
||||
import gtk.ToolItem;
|
||||
import gtk.Alignment;
|
||||
import peripheral.base;
|
||||
|
||||
System system;
|
||||
Label speedLabel;
|
||||
ToolItem speedItem;
|
||||
|
||||
// XXX FIXME XXX
|
||||
// the system and its ui components need to be separated
|
||||
// (so system can switch from ii to iie or whatever,
|
||||
// without affecting listeners/pausers/etc. that are connected to
|
||||
// the mainwindow)
|
||||
this()
|
||||
{
|
||||
super("Twoapple");
|
||||
|
||||
addOnKeyPress(&input.onKeyPress);
|
||||
addOnKeyRelease(&input.onKeyRelease);
|
||||
|
||||
auto mainBox = new VBox(false, 3);
|
||||
|
||||
auto menuBar = new MenuBar();
|
||||
auto menu = menuBar.append("_Menu");
|
||||
auto item = new MenuItem(&onMenuActivate, "_Read text file",
|
||||
"menu.read", true, null, 'r');
|
||||
menu.append(item);
|
||||
|
||||
auto toolBar = new Toolbar();
|
||||
toolBar.insert(input.pauseButton, -1);
|
||||
auto bootButton = new ToolButton(null, "Boot");
|
||||
toolBar.insert(bootButton, -1);
|
||||
bootButton.addOnClicked(&onBootClicked);
|
||||
toolBar.insert(new SeparatorToolItem());
|
||||
auto monoButton = new ToggleToolButton();
|
||||
monoButton.setLabel("Mono");
|
||||
monoButton.addOnToggled(&onMonoToggle);
|
||||
toolBar.insert(monoButton, -1);
|
||||
auto toolSpace = new SeparatorToolItem();
|
||||
toolSpace.setDraw(false);
|
||||
toolSpace.setExpand(true);
|
||||
toolBar.insert(toolSpace, -1);
|
||||
speedLabel = new Label(" 0%");
|
||||
speedItem = new ToolItem();
|
||||
speedItem.add(speedLabel);
|
||||
toolBar.insert(speedItem, -1);
|
||||
toolBar.setStyle(GtkToolbarStyle.TEXT);
|
||||
|
||||
mainBox.packStart(menuBar, false, false, 0);
|
||||
mainBox.packStart(toolBar, false, false, 0);
|
||||
mainBox.packStart(monitor, false, false, 0);
|
||||
mainBox.packStart(peripheralStatus, false, false, 0);
|
||||
|
||||
add(mainBox);
|
||||
setResizable(0);
|
||||
showAll();
|
||||
}
|
||||
|
||||
void onBootClicked(ToolButton b)
|
||||
{
|
||||
reboot = true;
|
||||
}
|
||||
|
||||
void onMonoToggle(ToggleToolButton b)
|
||||
{
|
||||
monitor.screen.isColor = (!b.getActive());
|
||||
if (input.pauseButton.getActive())
|
||||
{
|
||||
system.video_.forceFrame();
|
||||
}
|
||||
}
|
||||
|
||||
void initSystem(System sys)
|
||||
{
|
||||
showAll();
|
||||
system = sys;
|
||||
}
|
||||
|
||||
void onMenuActivate(MenuItem menuItem)
|
||||
{
|
||||
TwoappleFile checkFile =
|
||||
TwoappleFilePicker.open("text file");
|
||||
if (checkFile !is null)
|
||||
input.startTextInput(checkFile.fileName);
|
||||
}
|
||||
|
||||
int windowDelete(Event event, Widget widget)
|
||||
{
|
||||
stayOpen = false;
|
||||
soundCard.pause();
|
||||
return super.windowDelete(event, widget);
|
||||
}
|
||||
|
||||
bool configChanged;
|
||||
bool runOnce;
|
||||
bool stayOpen;
|
||||
bool reboot;
|
||||
|
||||
import std.stream;
|
||||
void mainLoop()
|
||||
{
|
||||
bool shouldRun;
|
||||
uint willElapse, didElapse;
|
||||
float speedPercent;
|
||||
char[] speedString = new char[10];
|
||||
stayOpen = true;
|
||||
do
|
||||
{
|
||||
if (configChanged)
|
||||
{
|
||||
// apply config
|
||||
configChanged = false;
|
||||
debug(disassemble)
|
||||
{
|
||||
reboot = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
reboot = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (reboot)
|
||||
{
|
||||
system.reboot();
|
||||
system.reset();
|
||||
input.pauseButton.setSensitive(true);
|
||||
input.pauseButton.setActive(false);
|
||||
host.delay.reset();
|
||||
reboot = false;
|
||||
}
|
||||
|
||||
if (input.pauseButton.getActive())
|
||||
shouldRun = runOnce;
|
||||
else
|
||||
shouldRun = true;
|
||||
|
||||
if (shouldRun)
|
||||
{
|
||||
willElapse = system.checkpoint();
|
||||
system.execute();
|
||||
didElapse = system.sinceCheckpoint(willElapse);
|
||||
input.processEvents();
|
||||
// XXX do something about typeahead?
|
||||
if (!runOnce)
|
||||
{
|
||||
host.delay.sleep();
|
||||
speedPercent = host.delay.checkSpeedPercent(didElapse);
|
||||
if (speedPercent > -1.0)
|
||||
{
|
||||
speedString = std.string.sformat(speedString,
|
||||
"% 5d%%", cast(int)(speedPercent * 100));
|
||||
speedLabel.setText(speedString);
|
||||
}
|
||||
}
|
||||
runOnce = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
input.processEvents();
|
||||
// XXX time says "Paused" or something
|
||||
if (!(input.pauseButton.getActive()))
|
||||
host.delay.reset();
|
||||
else
|
||||
host.delay.nap();
|
||||
}
|
||||
}
|
||||
while (stayOpen)
|
||||
}
|
||||
}
|
||||
|
||||
class TwoappleFile
|
||||
{
|
||||
import std.c.linux.linux;
|
||||
import std.file;
|
||||
import std.path;
|
||||
|
||||
string fileName;
|
||||
char* fileNameZ;
|
||||
|
||||
this(string fname)
|
||||
{
|
||||
assert(isabs(fname) != 0);
|
||||
fileName = fname;
|
||||
fileNameZ = std.string.toStringz(fname);
|
||||
}
|
||||
|
||||
string folder()
|
||||
{
|
||||
return getDirName(fileName);
|
||||
}
|
||||
|
||||
string baseName()
|
||||
{
|
||||
string base = getName(fileName);
|
||||
if (base is null) return getBaseName(fileName);
|
||||
else return getBaseName(base);
|
||||
}
|
||||
|
||||
bool canRead()
|
||||
{
|
||||
return !(access(fileNameZ, 4));
|
||||
}
|
||||
|
||||
bool canWrite()
|
||||
{
|
||||
return !(access(fileNameZ, 2));
|
||||
}
|
||||
|
||||
bool canCreate()
|
||||
{
|
||||
return !(access(std.string.toStringz(folder()), 2));
|
||||
}
|
||||
|
||||
bool exists()
|
||||
{
|
||||
return (std.file.exists(fileName) != 0);
|
||||
}
|
||||
|
||||
uint fileSize()
|
||||
{
|
||||
return getSize(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
class TwoappleError
|
||||
{
|
||||
import gtk.Window;
|
||||
import gtk.MessageDialog;
|
||||
|
||||
static void show(Window parent, string msg)
|
||||
{
|
||||
scope md = new MessageDialog(parent, GtkDialogFlags.MODAL,
|
||||
MessageType.ERROR, ButtonsType.CLOSE, msg);
|
||||
md.setTitle("Error");
|
||||
md.run();
|
||||
md.hide();
|
||||
md.destroy();
|
||||
}
|
||||
|
||||
static void show(string msg)
|
||||
{
|
||||
soundCard.pause();
|
||||
show(appWindow, msg);
|
||||
host.delay.reset();
|
||||
soundCard.resume();
|
||||
}
|
||||
}
|
||||
|
||||
class TwoappleFilePicker
|
||||
{
|
||||
import std.stdio;
|
||||
import gtk.FileChooserDialog;
|
||||
|
||||
static string[string] lastFolder;
|
||||
|
||||
static TwoappleFile saveAs(string type, string folder, string name)
|
||||
{
|
||||
soundCard.pause();
|
||||
|
||||
scope fcd = new FileChooserDialog("Save " ~ type, appWindow,
|
||||
FileChooserAction.SAVE);
|
||||
|
||||
scope chooser = fcd.getFileChooser();
|
||||
chooser.setCurrentFolder(folder);
|
||||
chooser.setCurrentName(name);
|
||||
|
||||
TwoappleFile file;
|
||||
while (true)
|
||||
{
|
||||
if (fcd.run() != ResponseType.GTK_RESPONSE_OK) break;
|
||||
file = new TwoappleFile(chooser.getFilename());
|
||||
if (file.exists())
|
||||
{
|
||||
if (!file.canWrite())
|
||||
{
|
||||
TwoappleError.show(fcd, "File is read-only");
|
||||
file = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!file.canCreate())
|
||||
{
|
||||
TwoappleError.show(fcd, "Directory is read-only");
|
||||
file = null;
|
||||
}
|
||||
}
|
||||
if (file !is null) break;
|
||||
}
|
||||
|
||||
fcd.hide();
|
||||
fcd.destroy();
|
||||
|
||||
host.delay.reset();
|
||||
soundCard.resume();
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
static TwoappleFile open(string type, string delegate(TwoappleFile) dg)
|
||||
{
|
||||
soundCard.pause();
|
||||
|
||||
scope fcd = new FileChooserDialog("Open " ~ type, appWindow,
|
||||
FileChooserAction.OPEN);
|
||||
scope chooser = fcd.getFileChooser();
|
||||
|
||||
if (type in lastFolder)
|
||||
chooser.setCurrentFolder(lastFolder[type]);
|
||||
|
||||
TwoappleFile file;
|
||||
while(true)
|
||||
{
|
||||
if (fcd.run() != ResponseType.GTK_RESPONSE_OK) break;
|
||||
file = new TwoappleFile(chooser.getFilename());
|
||||
if (!file.canRead())
|
||||
{
|
||||
TwoappleError.show(fcd, "File cannot be read");
|
||||
file = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
string msg = dg(file);
|
||||
if (msg !is null)
|
||||
{
|
||||
TwoappleError.show(fcd, msg);
|
||||
file = null;
|
||||
}
|
||||
}
|
||||
if (file !is null)
|
||||
{
|
||||
lastFolder[type] = file.folder();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fcd.hide();
|
||||
fcd.destroy();
|
||||
|
||||
host.delay.reset();
|
||||
soundCard.resume();
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
static TwoappleFile open(string type, string function(TwoappleFile) func)
|
||||
{
|
||||
return open(type,
|
||||
delegate string(TwoappleFile checkFile)
|
||||
{ return func(checkFile); });
|
||||
}
|
||||
|
||||
static TwoappleFile open(string type)
|
||||
{
|
||||
return open(type,
|
||||
delegate string(TwoappleFile checkFile)
|
||||
{ return null; });
|
||||
}
|
||||
}
|
||||
|
||||
class TwoappleDialog
|
||||
{
|
||||
import gtk.MessageDialog;
|
||||
|
||||
static int run(string title, string msg, string[] buttonText,
|
||||
bool hasDefault = true)
|
||||
{
|
||||
soundCard.pause();
|
||||
|
||||
scope md = new MessageDialog(appWindow, GtkDialogFlags.MODAL,
|
||||
MessageType.WARNING, ButtonsType.NONE, msg);
|
||||
md.setTitle(title);
|
||||
|
||||
int response;
|
||||
for (int b = 0; b < buttonText.length; ++b)
|
||||
{
|
||||
if ((b == 0) && hasDefault)
|
||||
response = ResponseType.GTK_RESPONSE_OK;
|
||||
else if (b == (buttonText.length - 1))
|
||||
response = ResponseType.GTK_RESPONSE_CANCEL;
|
||||
else
|
||||
response = b;
|
||||
md.addButton(buttonText[b], response);
|
||||
}
|
||||
|
||||
response = md.run();
|
||||
md.hide();
|
||||
md.destroy();
|
||||
|
||||
if (response == ResponseType.GTK_RESPONSE_OK)
|
||||
response = 0;
|
||||
else if ((response == ResponseType.GTK_RESPONSE_CANCEL) ||
|
||||
(response == ResponseType.GTK_RESPONSE_DELETE_EVENT))
|
||||
response = buttonText.length - 1;
|
||||
|
||||
host.delay.reset();
|
||||
soundCard.resume();
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
93
src/ui/monitor.d
Normal file
93
src/ui/monitor.d
Normal file
@ -0,0 +1,93 @@
|
||||
/+
|
||||
+ ui/monitor.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
|
||||
+/
|
||||
|
||||
private import gtk.DrawingArea;
|
||||
private import glgtk.GLCapability;
|
||||
private import glgdk.GLDrawable;
|
||||
private import glgdk.GLConfig;
|
||||
private import glgdk.GLContext;
|
||||
private import gtkglc.glgdktypes;
|
||||
private import gtk.Widget;
|
||||
private import gtk.ToggleToolButton;
|
||||
|
||||
import video.base;
|
||||
|
||||
Monitor monitor;
|
||||
|
||||
class Monitor : DrawingArea
|
||||
{
|
||||
mixin GLCapability;
|
||||
|
||||
Screen screen;
|
||||
|
||||
this()
|
||||
{
|
||||
setGLCapability(new GLConfig(
|
||||
GLConfigMode.MODE_RGB | GLConfigMode.MODE_DOUBLE,
|
||||
GLConfigMode.MODE_RGB));
|
||||
}
|
||||
|
||||
void installScreen(Screen screen_)
|
||||
{
|
||||
screen = screen_;
|
||||
setSizeRequest(screen.width, screen.height * 2);
|
||||
}
|
||||
|
||||
bool initGL()
|
||||
{
|
||||
resizeGL(null);
|
||||
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_FOG);
|
||||
glDisable(GL_LIGHTING);
|
||||
glDisable(GL_LOGIC_OP);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glDisable(GL_TEXTURE_1D);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
|
||||
glPixelZoom(1.0, -2.0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool drawGL(GdkEventExpose* event = null)
|
||||
{
|
||||
glRasterPos2i(-1, 1);
|
||||
glDrawPixels(screen.width, screen.height, GL_RGB,
|
||||
GL_UNSIGNED_SHORT_5_6_5, screen.data);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool resizeGL(GdkEventConfigure* event = null)
|
||||
{
|
||||
glViewport(0, 0, screen.width, screen.height * 2);
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
209
src/ui/sound.d
Normal file
209
src/ui/sound.d
Normal file
@ -0,0 +1,209 @@
|
||||
/+
|
||||
+ ui/sound.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 ui.sound;
|
||||
|
||||
import std.stdio;
|
||||
import std.c.string;
|
||||
|
||||
import derelict.sdl.sdl;
|
||||
import derelict.util.exception;
|
||||
|
||||
import timer;
|
||||
import device.speaker;
|
||||
import ui.inputevents;
|
||||
|
||||
static import host;
|
||||
|
||||
static this()
|
||||
{
|
||||
soundCard = new SoundCardNo();
|
||||
if (host.SDL)
|
||||
{
|
||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) == -1)
|
||||
{
|
||||
writefln("%s", std.string.toString(SDL_GetError()));
|
||||
return;
|
||||
}
|
||||
auto checkCard = new SoundCardYes();
|
||||
if (checkCard.isActive)
|
||||
soundCard = checkCard;
|
||||
}
|
||||
host.delay.soundCardActive = &soundCard.getIsActive;
|
||||
host.delay.soundCardHasEnoughData = &soundCard.hasEnoughData;
|
||||
}
|
||||
|
||||
SoundCard soundCard;
|
||||
|
||||
private:
|
||||
|
||||
class SoundCard
|
||||
{
|
||||
bool isActive;
|
||||
|
||||
// XXX hack for fast mode testing
|
||||
bool speedyMode;
|
||||
|
||||
bool getIsActive()
|
||||
{
|
||||
return isActive;
|
||||
}
|
||||
|
||||
abstract void installSpeaker(Speaker spkr);
|
||||
abstract void start();
|
||||
abstract void pause();
|
||||
abstract void resume();
|
||||
abstract void process();
|
||||
abstract bool hasEnoughData();
|
||||
}
|
||||
|
||||
class SoundCardNo : SoundCard
|
||||
{
|
||||
this()
|
||||
{
|
||||
isActive = false;
|
||||
}
|
||||
|
||||
void installSpeaker(Speaker spkr) {}
|
||||
void start() {}
|
||||
void pause() {}
|
||||
void resume() {}
|
||||
void process() {}
|
||||
bool hasEnoughData() { return true; } // XXX Throw exception?
|
||||
}
|
||||
|
||||
class SoundCardYes : SoundCard
|
||||
{
|
||||
SDL_AudioSpec audioRequest;
|
||||
short[] xferBuffer;
|
||||
short[] zeroBuffer;
|
||||
uint xferWriteIndex, xferReadIndex;
|
||||
Timer.Cycle soundCycle;
|
||||
Speaker speaker;
|
||||
|
||||
static const int samplesPerCallback = 1024;
|
||||
static const int sampleFreq = 44100;
|
||||
|
||||
this()
|
||||
{
|
||||
audioRequest.freq = sampleFreq;
|
||||
audioRequest.format = AUDIO_S16SYS;
|
||||
audioRequest.channels = 1;
|
||||
audioRequest.samples = samplesPerCallback;
|
||||
audioRequest.callback = &audioCallback;
|
||||
audioRequest.userdata = cast(void*)this;
|
||||
|
||||
if (SDL_OpenAudio(&audioRequest, null) == -1)
|
||||
{
|
||||
writefln("%s", std.string.toString(SDL_GetError()));
|
||||
return;
|
||||
}
|
||||
|
||||
xferBuffer.length = 8192;
|
||||
zeroBuffer.length = samplesPerCallback;
|
||||
isActive = true;
|
||||
}
|
||||
|
||||
void installSpeaker(Speaker spkr)
|
||||
{
|
||||
speaker = spkr;
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
SDL_PauseAudio(0);
|
||||
}
|
||||
|
||||
void pause()
|
||||
{
|
||||
SDL_PauseAudio(1);
|
||||
SDL_LockAudio();
|
||||
}
|
||||
|
||||
void resume()
|
||||
{
|
||||
SDL_UnlockAudio();
|
||||
SDL_PauseAudio(0);
|
||||
}
|
||||
|
||||
void process()
|
||||
{
|
||||
speaker.update();
|
||||
|
||||
if (!speedyMode) // XXX
|
||||
{
|
||||
SDL_LockAudio();
|
||||
|
||||
int inLength = speaker.mainIndex;
|
||||
short* inBuffer = speaker.mainBuffer.ptr;
|
||||
|
||||
// truncate xfer buffer if it would overflow
|
||||
if (xferWriteIndex + inLength > xferBuffer.length)
|
||||
{
|
||||
memmove(xferBuffer.ptr, xferBuffer.ptr + xferReadIndex,
|
||||
(xferBuffer.length - xferReadIndex) * 2);
|
||||
xferWriteIndex -= xferReadIndex;
|
||||
xferReadIndex = 0;
|
||||
}
|
||||
|
||||
memcpy(xferBuffer.ptr + xferWriteIndex, inBuffer,
|
||||
inLength * 2);
|
||||
xferWriteIndex += inLength;
|
||||
|
||||
SDL_UnlockAudio();
|
||||
}
|
||||
finishProcessing();
|
||||
}
|
||||
|
||||
void finishProcessing()
|
||||
{
|
||||
speaker.clearBuffer();
|
||||
}
|
||||
|
||||
bool hasEnoughData()
|
||||
{
|
||||
if (speedyMode) return false; // XXX
|
||||
SDL_LockAudio();
|
||||
uint bufLen = xferWriteIndex - xferReadIndex;
|
||||
SDL_UnlockAudio();
|
||||
return bufLen > samplesPerCallback;
|
||||
}
|
||||
|
||||
void fillAudio(Uint8* stream, int len)
|
||||
{
|
||||
int available = (xferWriteIndex - xferReadIndex) * 2;
|
||||
int readLen = (available > len) ? len : available;
|
||||
memcpy(stream, xferBuffer.ptr + xferReadIndex, readLen);
|
||||
if (input.pauseButton.getActive())
|
||||
{
|
||||
if (len > readLen)
|
||||
memcpy(stream + readLen, zeroBuffer.ptr, (len - readLen));
|
||||
}
|
||||
xferReadIndex += (readLen / 2);
|
||||
}
|
||||
}
|
||||
|
||||
extern(C) void audioCallback(void* userdata, Uint8* stream, int len)
|
||||
{
|
||||
(cast(SoundCardYes)userdata).fillAudio(stream, len);
|
||||
}
|
||||
|
79
src/ui/textinput.d
Normal file
79
src/ui/textinput.d
Normal file
@ -0,0 +1,79 @@
|
||||
/+
|
||||
+ ui/textinput.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 ui.textinput;
|
||||
|
||||
import std.stream;
|
||||
|
||||
import gtkglc.glgdktypes;
|
||||
import gtk.Widget;
|
||||
|
||||
class TextInput
|
||||
{
|
||||
File file;
|
||||
int pos;
|
||||
string line;
|
||||
void delegate() onFinish;
|
||||
bool eol;
|
||||
|
||||
this(string filename, void delegate() finished)
|
||||
{
|
||||
onFinish = finished;
|
||||
file = new File(filename);
|
||||
}
|
||||
|
||||
~this()
|
||||
{
|
||||
delete file;
|
||||
}
|
||||
|
||||
bool getLine()
|
||||
{
|
||||
if (file.eof())
|
||||
{
|
||||
onFinish();
|
||||
return false;
|
||||
}
|
||||
line = file.readLine() ~ x"0D";
|
||||
pos = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
ubyte read()
|
||||
{
|
||||
if (line is null)
|
||||
{
|
||||
if (!getLine())
|
||||
return 0;
|
||||
}
|
||||
return cast(ubyte)line[pos];
|
||||
}
|
||||
|
||||
void advancePos()
|
||||
{
|
||||
if (line is null) return;
|
||||
if (++pos >= line.length)
|
||||
{
|
||||
getLine();
|
||||
}
|
||||
}
|
||||
}
|
98
src/video/base.d
Normal file
98
src/video/base.d
Normal file
@ -0,0 +1,98 @@
|
||||
/+
|
||||
+ video/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 video.base;
|
||||
|
||||
import memory;
|
||||
|
||||
enum Mode
|
||||
{
|
||||
TEXT,
|
||||
LORES,
|
||||
HIRES
|
||||
}
|
||||
|
||||
class ScannerBase
|
||||
{
|
||||
abstract ubyte* getData(uint vidClock);
|
||||
abstract ubyte* getData(uint line, uint col);
|
||||
abstract ubyte* getData80(uint vidClock);
|
||||
abstract ubyte* getData80(uint line, uint col);
|
||||
abstract bool shouldUpdate();
|
||||
abstract uint currentLine();
|
||||
abstract uint currentCol();
|
||||
abstract Mode getMode();
|
||||
abstract bool checkColorBurst();
|
||||
abstract ubyte floatingBus(ushort addr);
|
||||
abstract void page2SwitchOn();
|
||||
abstract void page2SwitchOff();
|
||||
abstract void hiresSwitchOn();
|
||||
abstract void hiresSwitchOff();
|
||||
}
|
||||
|
||||
class PatternGenerator
|
||||
{
|
||||
ScannerBase scanner;
|
||||
abstract void initPatterns();
|
||||
abstract void initPatterns(ubyte[] rom);
|
||||
abstract void update(ubyte* signal, int scanLine, int startCol,
|
||||
uint len);
|
||||
abstract void update80(ubyte* signal, int scanLine, int startCol,
|
||||
uint len);
|
||||
}
|
||||
|
||||
class SignalBase
|
||||
{
|
||||
abstract void update();
|
||||
abstract void update(Mode mode, bool col80, bool colorBurst,
|
||||
int startLine, int stopLine, int startCol, int stopCol);
|
||||
abstract void dHGRChange(bool newdHGR);
|
||||
}
|
||||
|
||||
class Screen
|
||||
{
|
||||
ushort* data;
|
||||
int width, height;
|
||||
ScannerBase scanner;
|
||||
|
||||
bool isColor;
|
||||
|
||||
abstract void draw(bool col80, bool colorBurst, ubyte* bitmap,
|
||||
int scanLine, int scanStart, int scanEnd);
|
||||
}
|
||||
|
||||
struct VideoPages
|
||||
{
|
||||
DataMem lores1;
|
||||
DataMem lores2;
|
||||
DataMem hires1;
|
||||
DataMem hires2;
|
||||
|
||||
void reboot()
|
||||
{
|
||||
lores1.reboot();
|
||||
lores2.reboot();
|
||||
hires1.reboot();
|
||||
hires2.reboot();
|
||||
}
|
||||
}
|
||||
|
214
src/video/laz_engine.d
Normal file
214
src/video/laz_engine.d
Normal file
@ -0,0 +1,214 @@
|
||||
/+
|
||||
+ video/laz_engine.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 video.base;
|
||||
|
||||
private:
|
||||
|
||||
template Tuple(E...) { alias E Tuple; }
|
||||
|
||||
enum Color
|
||||
{
|
||||
Black,
|
||||
Red,
|
||||
Brown,
|
||||
Orange,
|
||||
DarkGreen,
|
||||
DarkGray,
|
||||
Green,
|
||||
Yellow,
|
||||
DarkBlue,
|
||||
Purple,
|
||||
LightGray,
|
||||
Pink,
|
||||
MediumBlue,
|
||||
LightBlue,
|
||||
Aqua,
|
||||
White
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
class LazEngine : Screen
|
||||
{
|
||||
ushort data_[562 * 192];
|
||||
|
||||
ushort[1][3] monoPalettes = [
|
||||
[0x03E0], // green
|
||||
[0xFAA3], // amber
|
||||
[0xFFFF] // white
|
||||
];
|
||||
ushort* monoPalette;
|
||||
|
||||
mixin("static final ushort[32][8] colorPalettes = "
|
||||
~ import("palette_5_6_5"));
|
||||
ushort* colorPalette;
|
||||
|
||||
mixin("static final uint[32][4][3] schemes = "
|
||||
~ import("laz_scheme"));
|
||||
uint[32]* scheme;
|
||||
|
||||
mixin("static final uint[32][2][2] sharp_table = "
|
||||
~ import("laz_sharp"));
|
||||
|
||||
this()
|
||||
{
|
||||
width = 562;
|
||||
height = 192;
|
||||
data = data_.ptr;
|
||||
monoPalette = monoPalettes[0].ptr;
|
||||
colorPalette = colorPalettes[0].ptr;
|
||||
scheme = schemes[0].ptr;
|
||||
isColor = true;
|
||||
}
|
||||
|
||||
void monochrome(ubyte* bitmap, int scanLine, int scanStart, int scanEnd,
|
||||
ushort color_5_6_5)
|
||||
{
|
||||
uint dataPtr = (scanLine * width) + scanStart * 14;
|
||||
uint bitmapPtr = 0;
|
||||
uint bits, bitPos;
|
||||
for (int scan = scanStart; scan <= scanEnd; ++scan)
|
||||
{
|
||||
foreach(mapByte; Tuple!(0, 1))
|
||||
{
|
||||
bits = bitmap[bitmapPtr++];
|
||||
bitPos = 0b01000000;
|
||||
foreach (mapBit; Tuple!(0, 1, 2, 3, 4, 5, 6))
|
||||
{
|
||||
if (bits & bitPos)
|
||||
data[dataPtr++] = color_5_6_5;
|
||||
else
|
||||
data[dataPtr++] = 0;
|
||||
bitPos >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw(bool col80, bool colorBurst, ubyte* bitmap, int scanLine,
|
||||
int scanStart, int scanEnd)
|
||||
{
|
||||
if (!isColor || !colorBurst)
|
||||
{
|
||||
monochrome(((scanStart == 0) ? bitmap : bitmap + 1),
|
||||
scanLine, scanStart, scanEnd,
|
||||
(isColor ? colorPalette[15] : monoPalette[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
polychrome(col80, bitmap, scanLine, scanStart, scanEnd);
|
||||
}
|
||||
}
|
||||
|
||||
void polychrome(bool col80, ubyte* bitmap, int scanLine, int scanStart,
|
||||
int scanEnd)
|
||||
{
|
||||
uint colStart = scanStart * 14;
|
||||
uint colEnd = scanEnd * 14 + 13;
|
||||
uint colCorrection = (col80 ? 0 : 3);
|
||||
bool finishCols;
|
||||
|
||||
uint bitmapPtr = 0;
|
||||
uint dataPtr = (scanLine * width) + colStart;
|
||||
|
||||
uint colorIndex, paletteIndex;
|
||||
uint prevBit, nextBit;
|
||||
uint nextBitTest = 0b00010000;
|
||||
uint nextBitShift = 4;
|
||||
|
||||
ubyte bits = bitmap[bitmapPtr];
|
||||
|
||||
if (scanStart == 0)
|
||||
{
|
||||
colorIndex = (bits >> 5) & 0b11;
|
||||
}
|
||||
else
|
||||
{
|
||||
colorIndex = (bits << 2) & 0b11111;
|
||||
bits = bitmap[++bitmapPtr];
|
||||
colorIndex |= (bits >> 5 & 0b11);
|
||||
}
|
||||
nextBit = (bits & nextBitTest) >> nextBitShift;
|
||||
|
||||
if (colEnd > 556)
|
||||
{
|
||||
finishCols = true;
|
||||
colEnd = 556;
|
||||
}
|
||||
colEnd += colCorrection;
|
||||
|
||||
for (uint col = colStart + colCorrection; col <= colEnd; ++col)
|
||||
{
|
||||
nextBitTest >>= 1;
|
||||
--nextBitShift;
|
||||
if (nextBitTest == 0)
|
||||
{
|
||||
nextBitTest = 0b01000000;
|
||||
nextBitShift = 6;
|
||||
bits = bitmap[++bitmapPtr];
|
||||
}
|
||||
prevBit = colorIndex >> 4;
|
||||
colorIndex = ((colorIndex << 1) | nextBit) & 0b11111;
|
||||
nextBit = (bits & nextBitTest) >> nextBitShift;
|
||||
paletteIndex = scheme[col & 0b11][colorIndex];
|
||||
if (paletteIndex > 0x80)
|
||||
{
|
||||
switch (sharp_table[prevBit][nextBit][colorIndex])
|
||||
{
|
||||
case 0:
|
||||
paletteIndex = Color.Black;
|
||||
case 15:
|
||||
paletteIndex = Color.White;
|
||||
case 99:
|
||||
paletteIndex &= 0x7F;
|
||||
}
|
||||
}
|
||||
data[dataPtr++] = colorPalette[paletteIndex];
|
||||
}
|
||||
|
||||
if (finishCols)
|
||||
{
|
||||
for (uint col = 557 + colCorrection; col <= 561 + colCorrection;
|
||||
++col)
|
||||
{
|
||||
prevBit = colorIndex >> 4;
|
||||
colorIndex = (colorIndex << 1) & 0b11111;
|
||||
paletteIndex = scheme[col & 0b11][colorIndex];
|
||||
if (paletteIndex > 0x80)
|
||||
{
|
||||
switch (sharp_table[prevBit][nextBit][colorIndex])
|
||||
{
|
||||
case 0:
|
||||
paletteIndex = Color.Black;
|
||||
case 15:
|
||||
paletteIndex = Color.White;
|
||||
case 99:
|
||||
paletteIndex &= 0x7F;
|
||||
}
|
||||
}
|
||||
data[dataPtr++] = colorPalette[paletteIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
171
src/video/offsets.d
Normal file
171
src/video/offsets.d
Normal file
@ -0,0 +1,171 @@
|
||||
/+
|
||||
+ video/offsets.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 video.base : Mode;
|
||||
|
||||
static this()
|
||||
{
|
||||
hCounter = new HorizontalCounter();
|
||||
vCounter = new VerticalCounter();
|
||||
|
||||
for (int scanLine = 0; scanLine < 192; ++scanLine)
|
||||
{
|
||||
firstDisplayedOffset[Mode.HIRES][scanLine] =
|
||||
screenOffset(&vCounter.states[scanLine], &hCounter.states[25],
|
||||
Mode.HIRES);
|
||||
firstDisplayedOffset[Mode.LORES][scanLine] =
|
||||
screenOffset(&vCounter.states[scanLine], &hCounter.states[25],
|
||||
Mode.LORES);
|
||||
firstDisplayedOffset[Mode.TEXT][scanLine] =
|
||||
screenOffset(&vCounter.states[scanLine], &hCounter.states[25],
|
||||
Mode.TEXT);
|
||||
}
|
||||
}
|
||||
|
||||
ushort scanOffset(uint vidClock, Mode mode)
|
||||
{
|
||||
if (((vidClock % 65) == 25) && (vidClock / 65) < 192)
|
||||
return firstDisplayedOffset[mode][vidClock / 65];
|
||||
else
|
||||
return screenOffset(&vCounter.states[vidClock / 65],
|
||||
&hCounter.states[vidClock % 65], mode);
|
||||
}
|
||||
|
||||
ushort scanOffset(uint line, uint col, Mode mode)
|
||||
{
|
||||
if ((col == 25) && (line < 192))
|
||||
return firstDisplayedOffset[mode][line];
|
||||
else
|
||||
return screenOffset(&vCounter.states[line], &hCounter.states[col],
|
||||
mode);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
HorizontalCounter hCounter;
|
||||
VerticalCounter vCounter;
|
||||
ushort[192][3] firstDisplayedOffset;
|
||||
|
||||
class HorizontalCounter
|
||||
{
|
||||
State[65] states;
|
||||
|
||||
this()
|
||||
{
|
||||
uint counter = 0b0000000;
|
||||
int number = 0;
|
||||
while(counter <= 0b1111111)
|
||||
{
|
||||
states[number++] = new State(counter);
|
||||
if (counter == 0b0000000)
|
||||
counter = 0b1000000;
|
||||
else
|
||||
++counter;
|
||||
}
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
HP = 0b1000000,
|
||||
H5 = 0b0100000,
|
||||
H4 = 0b0010000,
|
||||
H3 = 0b0001000,
|
||||
H2 = 0b0000100,
|
||||
H1 = 0b0000010,
|
||||
H0 = 0b0000001
|
||||
}
|
||||
|
||||
class State
|
||||
{
|
||||
int a6_a5_a4_a3;
|
||||
int a2_a1_a0;
|
||||
|
||||
this(int counter)
|
||||
{
|
||||
a6_a5_a4_a3 = counter & (H5 | H4 | H3);
|
||||
a2_a1_a0 = counter & (H2 | H1 | H0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VerticalCounter
|
||||
{
|
||||
State[] states;
|
||||
|
||||
this()
|
||||
{
|
||||
uint counter = 0b011111010; // XXX PAL: 0b011001000
|
||||
states.length = 512 - counter;
|
||||
int scanline = 256;
|
||||
while (counter <= 0b111111111)
|
||||
{
|
||||
states[scanline++] = new State(counter++);
|
||||
if (counter == 0b100000000) scanline = 0;
|
||||
}
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
V5 = 0b100000000,
|
||||
V4 = 0b010000000,
|
||||
V3 = 0b001000000,
|
||||
V2 = 0b000100000,
|
||||
V1 = 0b000010000,
|
||||
V0 = 0b000001000,
|
||||
VC = 0b000000100,
|
||||
VB = 0b000000010,
|
||||
VA = 0b000000001
|
||||
}
|
||||
|
||||
class State
|
||||
{
|
||||
int a12_a11_a10;
|
||||
int a9_a8_a7;
|
||||
int a6_a5_a4_a3;
|
||||
|
||||
this(int counter)
|
||||
{
|
||||
a12_a11_a10 = (counter & (VC | VB | VA)) << 10;
|
||||
a9_a8_a7 = (counter & (V2 | V1 | V0)) << 4;
|
||||
a6_a5_a4_a3 = ((counter & (V4 | V3)) >> 1) |
|
||||
((counter & (V4 | V3)) >> 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
alias VerticalCounter.State vState;
|
||||
alias HorizontalCounter.State hState;
|
||||
|
||||
ushort screenOffset(vState* vSt, hState* hSt, Mode mode)
|
||||
{
|
||||
ushort sum_a6_a5_a4_a3 =
|
||||
(vSt.a6_a5_a4_a3 + hSt.a6_a5_a4_a3 + 0b1101000) & 0b1111000;
|
||||
ushort offset = vSt.a9_a8_a7 | sum_a6_a5_a4_a3 | hSt.a2_a1_a0;
|
||||
|
||||
if (mode == Mode.HIRES)
|
||||
{
|
||||
offset |= vSt.a12_a11_a10;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
485
src/video/patterns.d
Normal file
485
src/video/patterns.d
Normal file
@ -0,0 +1,485 @@
|
||||
/+
|
||||
+ video/patterns.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 video.base;
|
||||
import device.base;
|
||||
import memory;
|
||||
import timer;
|
||||
|
||||
static this()
|
||||
{
|
||||
initDoubleBits();
|
||||
initReverseBits();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
ubyte[2][128] doubleBits;
|
||||
ubyte[128] reverseBits;
|
||||
|
||||
void initDoubleBits()
|
||||
{
|
||||
for (int bits = 0; bits < 128; ++bits)
|
||||
{
|
||||
for (int newBit = 13; newBit >= 0; --newBit)
|
||||
{
|
||||
int index = 1 - (newBit / 7);
|
||||
doubleBits[bits][index] <<= 1;
|
||||
if (bits & (1 << (newBit / 2)))
|
||||
doubleBits[bits][index] |= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void initReverseBits()
|
||||
{
|
||||
for (int bits = 0; bits < 128; ++bits)
|
||||
{
|
||||
for (int bitPos = 0; bitPos < 7 ; ++bitPos)
|
||||
{
|
||||
reverseBits[bits] <<= 1;
|
||||
if ((bits & (1 << bitPos)) != 0)
|
||||
reverseBits[bits] |= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final ubyte INVERSE = 0b01111111;
|
||||
|
||||
public:
|
||||
|
||||
class TextPatternGenerator : PatternGenerator
|
||||
{
|
||||
ubyte[2][8][256] flashOffPatterns;
|
||||
ubyte[2][8][256] flashOnPatterns;
|
||||
ubyte[2][8][256] flashNullPatterns;
|
||||
ubyte[8][256] flashOffPatterns80;
|
||||
ubyte[8][256] flashOnPatterns80;
|
||||
ubyte[8][256] flashNullPatterns80;
|
||||
|
||||
ubyte[2][8][256]* patterns;
|
||||
ubyte[8][256]* patterns80;
|
||||
|
||||
bool flash, altCharset;
|
||||
|
||||
SignalBase signal;
|
||||
ScannerBase scanner;
|
||||
ubyte delegate() kbdLatch;
|
||||
|
||||
this()
|
||||
{
|
||||
patterns = &flashOffPatterns;
|
||||
patterns80 = &flashOffPatterns80;
|
||||
}
|
||||
|
||||
bool toggleFlash()
|
||||
{
|
||||
flash = !flash;
|
||||
if (!altCharset)
|
||||
{
|
||||
if (scanner.getMode() == Mode.TEXT)
|
||||
{
|
||||
signal.update();
|
||||
}
|
||||
applyFlash();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void altCharsetChange(bool newState)
|
||||
{
|
||||
if (altCharset == newState) return;
|
||||
if (scanner.getMode() == Mode.TEXT)
|
||||
{
|
||||
signal.update();
|
||||
}
|
||||
altCharset = newState;
|
||||
if (newState)
|
||||
{
|
||||
patterns = &flashNullPatterns;
|
||||
patterns80 = &flashNullPatterns80;
|
||||
}
|
||||
else applyFlash();
|
||||
}
|
||||
|
||||
void applyFlash()
|
||||
{
|
||||
patterns = (flash ? &flashOnPatterns : &flashOffPatterns);
|
||||
patterns80 = (flash ? &flashOnPatterns80 : &flashOffPatterns80);
|
||||
}
|
||||
|
||||
void update(ubyte* signal, int scanLine, int startCol, uint len)
|
||||
{
|
||||
uint screenPos = 0;
|
||||
uint dotLine = scanLine % 8;
|
||||
uint memByte;
|
||||
ubyte* data = scanner.getData(scanLine, startCol + 25);
|
||||
|
||||
for (uint offset = 0; offset < len; ++offset)
|
||||
{
|
||||
memByte = data[offset];
|
||||
signal[screenPos++] = (*patterns)[memByte][dotLine][0];
|
||||
signal[screenPos++] = (*patterns)[memByte][dotLine][1];
|
||||
}
|
||||
}
|
||||
|
||||
void update80(ubyte* signal, int scanLine, int startCol, uint len)
|
||||
{
|
||||
uint screenPos = 0;
|
||||
uint dotLine = scanLine % 8;
|
||||
ubyte* data = scanner.getData(scanLine, startCol + 25);
|
||||
ubyte* auxData = scanner.getData80(scanLine, startCol + 25);
|
||||
|
||||
for (uint offset = 0; offset < len; ++offset)
|
||||
{
|
||||
signal[screenPos++] = (*patterns80)[auxData[offset]][dotLine];
|
||||
signal[screenPos++] = (*patterns80)[data[offset]][dotLine];
|
||||
}
|
||||
}
|
||||
|
||||
void reboot()
|
||||
{
|
||||
altCharsetChange(false);
|
||||
}
|
||||
|
||||
mixin(EmptyInitSwitches());
|
||||
|
||||
abstract void init(Timer timer);
|
||||
}
|
||||
|
||||
class TextPatternGenerator_II : TextPatternGenerator
|
||||
{
|
||||
mixin("static final ubyte[256] charsetUpper_II = "
|
||||
~ import("charset_upper_ii"));
|
||||
mixin("static final ubyte[256] charsetSymbol_II = "
|
||||
~ import("charset_symbol_ii"));
|
||||
|
||||
void initPatterns()
|
||||
{
|
||||
ubyte[][] segments = new ubyte[][8];
|
||||
segments[0] = segments[2] = segments[4] = segments[6] =
|
||||
charsetUpper_II;
|
||||
segments[1] = segments[3] = segments[5] = segments[7] =
|
||||
charsetSymbol_II;
|
||||
|
||||
ubyte flashOff, flashOn;
|
||||
for (int seg = 0; seg < 8; ++seg)
|
||||
{
|
||||
foreach(index, pattern; segments[seg])
|
||||
{
|
||||
uint ascii = (((index / 32) * 4) + (index % 4)) + (seg * 32);
|
||||
uint dotLine = (index / 4) % 8;
|
||||
switch (seg)
|
||||
{
|
||||
case 0, 1:
|
||||
flashOff = flashOn = (pattern ^ INVERSE);
|
||||
break;
|
||||
case 2, 3:
|
||||
flashOff = pattern;
|
||||
flashOn = (pattern ^ INVERSE);
|
||||
break;
|
||||
default:
|
||||
flashOff = flashOn = pattern;
|
||||
break;
|
||||
}
|
||||
flashOffPatterns[ascii][dotLine][0] = doubleBits[flashOff][0];
|
||||
flashOffPatterns[ascii][dotLine][1] = doubleBits[flashOff][1];
|
||||
flashOnPatterns[ascii][dotLine][0] = doubleBits[flashOn][0];
|
||||
flashOnPatterns[ascii][dotLine][1] = doubleBits[flashOn][1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void initPatterns(ubyte[] rom)
|
||||
{
|
||||
/+ TODO: Initialize patterns from character generator rom +/
|
||||
if (rom is null) initPatterns();
|
||||
else initPatterns();
|
||||
}
|
||||
|
||||
// XXX XXX INIT
|
||||
void init(Timer timer)
|
||||
{
|
||||
timer.new Counter(timer.hertz / 2, &toggleFlash);
|
||||
}
|
||||
}
|
||||
|
||||
class TextPatternGenerator_IIe : TextPatternGenerator
|
||||
{
|
||||
mixin("static final ubyte[256] charsetUpper_IIe = "
|
||||
~ import("charset_upper_iie"));
|
||||
mixin("static final ubyte[256] charsetSymbol_IIe = "
|
||||
~ import("charset_symbol_iie"));
|
||||
mixin("static final ubyte[256] charsetLower = "
|
||||
~ import("charset_lower"));
|
||||
mixin("static final ubyte[256] charsetMouse = "
|
||||
~ import("charset_mouse"));
|
||||
|
||||
void initPatterns()
|
||||
{
|
||||
ubyte[][] segments = new ubyte[][8];
|
||||
segments[0] = segments[4] = segments[6] = charsetUpper_IIe;
|
||||
segments[1] = segments[5] = charsetSymbol_IIe;
|
||||
segments[2] = charsetMouse; // unenhanced: charsetUpper
|
||||
segments[3] = segments[7] = charsetLower;
|
||||
|
||||
ubyte flashNull;
|
||||
for (int seg = 0; seg < 8; ++seg)
|
||||
{
|
||||
foreach(index, pattern; segments[seg])
|
||||
{
|
||||
uint ascii = (((index / 32) * 4) + (index % 4)) + (seg * 32);
|
||||
uint dotLine = (index / 4) % 8;
|
||||
switch (seg)
|
||||
{
|
||||
case 0, 1, 3:
|
||||
flashNull = pattern ^ INVERSE;
|
||||
break;
|
||||
case 2:
|
||||
flashNull = pattern; // unenhanced: pattern ^ INVERSE
|
||||
break;
|
||||
default:
|
||||
flashNull = pattern;
|
||||
break;
|
||||
}
|
||||
flashNullPatterns[ascii][dotLine][0] =
|
||||
doubleBits[flashNull][0];
|
||||
flashNullPatterns[ascii][dotLine][1] =
|
||||
doubleBits[flashNull][1];
|
||||
flashNullPatterns80[ascii][dotLine] = flashNull;
|
||||
}
|
||||
}
|
||||
|
||||
ubyte flashOn, flashOff;
|
||||
for (uint ascii = 0; ascii < 256; ++ascii)
|
||||
{
|
||||
for (uint dotLine = 0; dotLine < 8; ++dotLine)
|
||||
{
|
||||
if ((ascii >= 64) && (ascii < 128))
|
||||
{
|
||||
flashOn = flashNullPatterns80[ascii - 64][dotLine];
|
||||
flashOff = flashNullPatterns80[ascii + 64][dotLine];
|
||||
}
|
||||
else
|
||||
{
|
||||
flashOn = flashOff = flashNullPatterns80[ascii][dotLine];
|
||||
}
|
||||
flashOffPatterns[ascii][dotLine][0] = doubleBits[flashOff][0];
|
||||
flashOffPatterns[ascii][dotLine][1] = doubleBits[flashOff][1];
|
||||
flashOffPatterns80[ascii][dotLine] = flashOff;
|
||||
flashOnPatterns[ascii][dotLine][0] = doubleBits[flashOn][0];
|
||||
flashOnPatterns[ascii][dotLine][1] = doubleBits[flashOn][1];
|
||||
flashOnPatterns80[ascii][dotLine] = flashOn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void initPatterns(ubyte[] rom)
|
||||
{
|
||||
/+ TODO: Initialize patterns from character generator rom +/
|
||||
if (rom is null) initPatterns();
|
||||
else initPatterns();
|
||||
}
|
||||
|
||||
// XXX XXX INIT
|
||||
void init(Timer timer)
|
||||
{
|
||||
timer.new Counter(32 * 262 * 65, &toggleFlash); // XXX PAL
|
||||
}
|
||||
|
||||
void altCharsetOn()
|
||||
{
|
||||
altCharsetChange(true);
|
||||
}
|
||||
|
||||
void altCharsetOff()
|
||||
{
|
||||
altCharsetChange(false);
|
||||
}
|
||||
|
||||
ubyte readAltCharset()
|
||||
{
|
||||
return kbdLatch() | (altCharset ? 0x80 : 0x00);
|
||||
}
|
||||
|
||||
// XXX XXX INIT
|
||||
mixin(InitSwitches("", [
|
||||
mixin(MakeSwitch([0xC00E], "W", "altCharsetOff")),
|
||||
mixin(MakeSwitch([0xC00F], "W", "altCharsetOn")),
|
||||
mixin(MakeSwitch([0xC01E], "R", "readAltCharset"))
|
||||
]));
|
||||
}
|
||||
|
||||
class LoresPatternGenerator : PatternGenerator
|
||||
{
|
||||
ubyte[2][2][16] patterns;
|
||||
|
||||
void initPatterns()
|
||||
{
|
||||
int[16] dhrBits4 = [
|
||||
0x0, 0x8, 0x4, 0xC, 0x2, 0x5, 0x6, 0xE,
|
||||
0x1, 0x9, 0xA, 0xD, 0x3, 0xB, 0x7, 0xF];
|
||||
for (int bits4 = 0; bits4 < 16; ++bits4)
|
||||
{
|
||||
ubyte bits8 = (dhrBits4[bits4] << 4) | dhrBits4[bits4];
|
||||
ushort bits16 = (bits8 << 8) | bits8;
|
||||
uint bits32 = (bits16 << 16) | bits16;
|
||||
patterns[bits4][0][0] = bits32 >> 25;
|
||||
patterns[bits4][0][1] = (bits32 >> 18) & 0b01111111;
|
||||
patterns[bits4][1][0] = (bits32 >> 11) & 0b01111111;
|
||||
patterns[bits4][1][1] = (bits32 >> 4) & 0b01111111;
|
||||
}
|
||||
}
|
||||
|
||||
void initPatterns(ubyte[] rom)
|
||||
{
|
||||
/+ TODO: Initialize patterns from IIe video ROM +/
|
||||
if (rom is null) initPatterns();
|
||||
else initPatterns();
|
||||
}
|
||||
|
||||
void update(ubyte* signal, int scanLine, int startCol, uint len)
|
||||
{
|
||||
uint screenPos = 0;
|
||||
uint col = startCol;
|
||||
int shiftAmt = (((scanLine % 8) / 4) == 0) ? 0 : 4;
|
||||
ubyte* data = scanner.getData(scanLine, startCol + 25);
|
||||
uint memNybble;
|
||||
|
||||
for (uint offset = 0; offset < len; ++offset)
|
||||
{
|
||||
memNybble = (data[offset] >> shiftAmt) & 0x0F;
|
||||
signal[screenPos++] = patterns[memNybble][col & 1][0];
|
||||
signal[screenPos++] = patterns[memNybble][col++ & 1][1];
|
||||
}
|
||||
}
|
||||
|
||||
void update80(ubyte* signal, int scanLine, int startCol, uint len)
|
||||
{
|
||||
uint screenPos = 0;
|
||||
uint col = startCol;
|
||||
int shiftAmt = (((scanLine % 8) / 4) == 0) ? 0 : 4;
|
||||
ubyte* data = scanner.getData(scanLine, startCol + 25);
|
||||
ubyte* auxData = scanner.getData80(scanLine, startCol + 25);
|
||||
uint memNybble;
|
||||
|
||||
for (uint offset = 0; offset < len; ++offset)
|
||||
{
|
||||
memNybble = (auxData[offset] >> shiftAmt) & 0x0F;
|
||||
signal[screenPos++] = patterns[memNybble][col & 1][0];
|
||||
|
||||
memNybble = (data[offset] >> shiftAmt) & 0x0F;
|
||||
signal[screenPos++] = patterns[memNybble][col++ & 1][0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class HiresPatternGenerator : PatternGenerator
|
||||
{
|
||||
ubyte[2][256] patterns;
|
||||
ubyte[256] patterns80;
|
||||
|
||||
ubyte prevByte;
|
||||
|
||||
void initPatterns()
|
||||
{
|
||||
for (int bits = 0; bits < 256; ++bits)
|
||||
{
|
||||
ubyte leftPattern = doubleBits[reverseBits[bits & 0x7F]][0];
|
||||
ubyte rightPattern = doubleBits[reverseBits[bits & 0x7F]][1];
|
||||
if (bits > 0x7F)
|
||||
{
|
||||
patterns[bits][0] = leftPattern >> 1;
|
||||
patterns[bits][1] = (rightPattern >> 1) |
|
||||
((leftPattern & 1) << 6);
|
||||
}
|
||||
else
|
||||
{
|
||||
patterns[bits][0] = leftPattern;
|
||||
patterns[bits][1] = rightPattern;
|
||||
}
|
||||
|
||||
patterns80[bits] = reverseBits[bits & 0x7F] | (bits & 0x80);
|
||||
}
|
||||
}
|
||||
|
||||
void initPatterns(ubyte[] rom)
|
||||
{
|
||||
/+ TODO: Initialize patterns from IIe video ROM +/
|
||||
if (rom is null) initPatterns();
|
||||
else initPatterns();
|
||||
}
|
||||
|
||||
void update(ubyte* signal, int scanLine, int startCol, uint len)
|
||||
{
|
||||
uint screenPos = 0;
|
||||
uint vidClock = scanLine * 65 + startCol + 25;
|
||||
ubyte* data = scanner.getData(vidClock);
|
||||
uint memByte;
|
||||
|
||||
if (startCol == 0)
|
||||
prevByte = *(scanner.getData(vidClock - 1)) & 0b01000000;
|
||||
|
||||
for (uint offset = 0; offset < len; ++offset)
|
||||
{
|
||||
memByte = data[offset];
|
||||
|
||||
if (memByte > 0x7F)
|
||||
signal[screenPos++] = patterns[memByte][0] | prevByte;
|
||||
else
|
||||
signal[screenPos++] = patterns[memByte][0];
|
||||
prevByte = memByte & 0b01000000;
|
||||
signal[screenPos++] = patterns[memByte][1];
|
||||
}
|
||||
}
|
||||
|
||||
void update80(ubyte* signal, int scanLine, int startCol, uint len)
|
||||
{
|
||||
uint screenPos = 0;
|
||||
ubyte* data = scanner.getData(scanLine, startCol + 25);
|
||||
ubyte* auxData = scanner.getData80(scanLine, startCol + 25);
|
||||
|
||||
for (int offset = 0; offset < len; ++offset)
|
||||
{
|
||||
signal[screenPos++] = patterns80[auxData[offset]];
|
||||
signal[screenPos++] = patterns80[data[offset]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class HiresPatternGenerator_Revision0 : HiresPatternGenerator
|
||||
{
|
||||
void update(ubyte* signal, int scanLine, int startCol, uint len)
|
||||
{
|
||||
uint screenPos = 0;
|
||||
ubyte* data = scanner.getData(scanLine, startCol);
|
||||
uint memByte;
|
||||
|
||||
for (int offset = 0; offset < len; ++offset)
|
||||
{
|
||||
memByte = data[offset] & 0x7F;
|
||||
signal[screenPos++] = patterns[memByte][0];
|
||||
signal[screenPos++] = patterns[memByte][1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
295
src/video/scanner.d
Normal file
295
src/video/scanner.d
Normal file
@ -0,0 +1,295 @@
|
||||
/+
|
||||
+ video/scanner.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 video.base;
|
||||
import video.offsets;
|
||||
import memory;
|
||||
import timer;
|
||||
import device.base;
|
||||
|
||||
class Scanner : ScannerBase
|
||||
{
|
||||
uint page;
|
||||
bool graphicsTime, textSwitch, mixedSwitch, hiresSwitch, oldTextSwitch;
|
||||
Mode mode;
|
||||
|
||||
Timer.Cycle vidCycle;
|
||||
uint frameSkip, frameCount;
|
||||
|
||||
DataMem[2][3] displayMem;
|
||||
DataMem[2][3] auxDisplayMem;
|
||||
|
||||
SignalBase signal;
|
||||
ubyte delegate() kbdLatch;
|
||||
bool delegate() drawFrame;
|
||||
|
||||
void init(Timer timer)
|
||||
{
|
||||
int frameLen = 262 * 65; // XXX PAL: 312 * 65
|
||||
vidCycle = timer.startCycle(frameLen);
|
||||
graphicsTime = true;
|
||||
|
||||
timer.new Counter(frameLen, &graphicsTimeOn);
|
||||
timer.new DelayedCounter(frameLen, &frameComplete, frameLen - 1);
|
||||
timer.new DelayedCounter(frameLen, &graphicsTimeOff, 160 * 65);
|
||||
timer.new DelayedCounter(frameLen, &graphicsTimeOn, 192 * 65);
|
||||
timer.new DelayedCounter(frameLen, &graphicsTimeOff, 224 * 65);
|
||||
}
|
||||
|
||||
void forceFrame()
|
||||
{
|
||||
}
|
||||
|
||||
void reboot()
|
||||
{
|
||||
page2SwitchOff();
|
||||
hiresSwitchOff();
|
||||
mixedSwitchOff();
|
||||
textSwitchOn();
|
||||
}
|
||||
|
||||
void installMemory(DataMem loresPage1, DataMem loresPage2,
|
||||
DataMem hiresPage1, DataMem hiresPage2)
|
||||
{
|
||||
displayMem[Mode.TEXT][0] = loresPage1;
|
||||
displayMem[Mode.TEXT][1] = loresPage2;
|
||||
displayMem[Mode.LORES][0] = loresPage1;
|
||||
displayMem[Mode.LORES][1] = loresPage2;
|
||||
displayMem[Mode.HIRES][0] = hiresPage1;
|
||||
displayMem[Mode.HIRES][1] = hiresPage2;
|
||||
}
|
||||
|
||||
void installAuxMemory(DataMem loresPage1, DataMem loresPage2,
|
||||
DataMem hiresPage1, DataMem hiresPage2)
|
||||
{
|
||||
auxDisplayMem[Mode.TEXT][0] = loresPage1;
|
||||
auxDisplayMem[Mode.TEXT][1] = loresPage2;
|
||||
auxDisplayMem[Mode.LORES][0] = loresPage1;
|
||||
auxDisplayMem[Mode.LORES][1] = loresPage2;
|
||||
auxDisplayMem[Mode.HIRES][0] = hiresPage1;
|
||||
auxDisplayMem[Mode.HIRES][1] = hiresPage2;
|
||||
}
|
||||
|
||||
bool graphicsTimeOn()
|
||||
{
|
||||
changeMode({graphicsTime = true;});
|
||||
return true;
|
||||
}
|
||||
|
||||
bool graphicsTimeOff()
|
||||
{
|
||||
changeMode({graphicsTime = false;});
|
||||
return true;
|
||||
}
|
||||
|
||||
bool frameComplete()
|
||||
{
|
||||
signal.update();
|
||||
if (shouldUpdate()) drawFrame();
|
||||
frameCount = ((frameCount + 1) % (frameSkip + 1));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool shouldUpdate()
|
||||
{
|
||||
return ((frameCount % (frameSkip + 1)) == 0);
|
||||
}
|
||||
|
||||
void changeMode(void delegate() change)
|
||||
{
|
||||
oldTextSwitch = textSwitch;
|
||||
change();
|
||||
Mode newMode = changedMode();
|
||||
if (newMode != mode)
|
||||
{
|
||||
signal.update();
|
||||
}
|
||||
mode = newMode;
|
||||
oldTextSwitch = textSwitch;
|
||||
}
|
||||
|
||||
Mode changedMode()
|
||||
{
|
||||
if (textSwitch) return Mode.TEXT;
|
||||
if (mixedSwitch && !graphicsTime) return Mode.TEXT;
|
||||
|
||||
if (hiresSwitch) return Mode.HIRES;
|
||||
else return Mode.LORES;
|
||||
}
|
||||
|
||||
void textSwitchOff()
|
||||
{
|
||||
changeMode({textSwitch = false;});
|
||||
}
|
||||
|
||||
void textSwitchOn()
|
||||
{
|
||||
changeMode({textSwitch = true;});
|
||||
}
|
||||
|
||||
void mixedSwitchOff()
|
||||
{
|
||||
changeMode({mixedSwitch = false;});
|
||||
}
|
||||
|
||||
void mixedSwitchOn()
|
||||
{
|
||||
changeMode({mixedSwitch = true;});
|
||||
}
|
||||
|
||||
void hiresSwitchOff()
|
||||
{
|
||||
changeMode({hiresSwitch = false;});
|
||||
}
|
||||
|
||||
void hiresSwitchOn()
|
||||
{
|
||||
changeMode({hiresSwitch = true;});
|
||||
}
|
||||
|
||||
void page2SwitchOff()
|
||||
{
|
||||
if (page == 1)
|
||||
{
|
||||
signal.update();
|
||||
page = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void page2SwitchOn()
|
||||
{
|
||||
if (page == 0)
|
||||
{
|
||||
signal.update();
|
||||
page = 1;
|
||||
}
|
||||
}
|
||||
|
||||
Mode getMode()
|
||||
{
|
||||
return mode;
|
||||
}
|
||||
|
||||
bool checkColorBurst()
|
||||
{
|
||||
return !oldTextSwitch;
|
||||
/+ TODO: For "pretty" mixed mode, return (mode != Mode.TEXT) +/
|
||||
}
|
||||
|
||||
uint currentLine()
|
||||
{
|
||||
return vidCycle.currentVal() / 65;
|
||||
}
|
||||
|
||||
uint currentCol()
|
||||
{
|
||||
return vidCycle.currentVal() % 65;
|
||||
}
|
||||
|
||||
ubyte* getData(uint vidClock)
|
||||
{
|
||||
return displayMem[mode][page].data + scanOffset(vidClock, mode);
|
||||
}
|
||||
|
||||
ubyte* getData(uint line, uint col)
|
||||
{
|
||||
return displayMem[mode][page].data + scanOffset(line, col, mode);
|
||||
}
|
||||
|
||||
ubyte* getData80(uint vidClock)
|
||||
{
|
||||
return auxDisplayMem[mode][page].data + scanOffset(vidClock, mode);
|
||||
}
|
||||
|
||||
ubyte* getData80(uint line, uint col)
|
||||
{
|
||||
return auxDisplayMem[mode][page].data + scanOffset(line, col, mode);
|
||||
}
|
||||
|
||||
mixin(InitSwitches("", [
|
||||
mixin(MakeSwitch([0xC050], "R0W", "textSwitchOff")),
|
||||
mixin(MakeSwitch([0xC051], "R0W", "textSwitchOn")),
|
||||
mixin(MakeSwitch([0xC052], "R0W", "mixedSwitchOff")),
|
||||
mixin(MakeSwitch([0xC053], "R0W", "mixedSwitchOn"))
|
||||
]));
|
||||
}
|
||||
|
||||
class Scanner_II : Scanner
|
||||
{
|
||||
import memory: AddressDecoder;
|
||||
|
||||
AddressDecoder decoder;
|
||||
|
||||
ubyte floatingBus(ushort addr)
|
||||
{
|
||||
uint clock = vidCycle.currentVal();
|
||||
if (((clock % 65) < 25) && (mode != Mode.HIRES))
|
||||
return decoder.read(0x1400 + (page * 0x400) +
|
||||
scanOffset(clock, mode));
|
||||
else
|
||||
return displayMem[mode][page].data[scanOffset(clock, mode)];
|
||||
}
|
||||
|
||||
mixin(InitSwitches("super", [
|
||||
mixin(MakeSwitch([0xC054], "R0W", "page2SwitchOff")),
|
||||
mixin(MakeSwitch([0xC055], "R0W", "page2SwitchOn")),
|
||||
mixin(MakeSwitch([0xC056], "R0W", "hiresSwitchOff")),
|
||||
mixin(MakeSwitch([0xC057], "R0W", "hiresSwitchOn"))
|
||||
]));
|
||||
}
|
||||
|
||||
class Scanner_IIe : Scanner
|
||||
{
|
||||
ubyte floatingBus(ushort addr)
|
||||
{
|
||||
return displayMem[mode][page].data[scanOffset(vidCycle.currentVal(),
|
||||
mode)];
|
||||
// equivalent to getData()[0];
|
||||
}
|
||||
|
||||
ubyte readText()
|
||||
{
|
||||
return kbdLatch() | (textSwitch ? 0x80 : 0x00);
|
||||
}
|
||||
|
||||
ubyte readMixed()
|
||||
{
|
||||
return kbdLatch() | (mixedSwitch ? 0x80 : 0x00);
|
||||
}
|
||||
|
||||
bool readVBL()
|
||||
{
|
||||
return (vidCycle.currentVal() >= (192 * 65));
|
||||
}
|
||||
|
||||
ubyte readLowVBL()
|
||||
{
|
||||
return kbdLatch() | ((!readVBL()) ? 0x80 : 0x00);
|
||||
}
|
||||
|
||||
mixin(InitSwitches("super", [
|
||||
mixin(MakeSwitch([0xC019], "R", "readLowVBL")),
|
||||
mixin(MakeSwitch([0xC01A], "R", "readText")),
|
||||
mixin(MakeSwitch([0xC01B], "R", "readMixed"))
|
||||
]));
|
||||
}
|
||||
|
207
src/video/signal.d
Normal file
207
src/video/signal.d
Normal file
@ -0,0 +1,207 @@
|
||||
/+
|
||||
+ video/signal.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 video.base;
|
||||
import video.patterns;
|
||||
import device.base;
|
||||
import memory;
|
||||
import timer;
|
||||
|
||||
class Signal : SignalBase
|
||||
{
|
||||
ubyte[560 * 192] bitmap;
|
||||
int nextLine, nextCol;
|
||||
bool col80Switch, dHGR;
|
||||
|
||||
Screen screen;
|
||||
ScannerBase scanner;
|
||||
ubyte delegate() kbdLatch;
|
||||
|
||||
PatternGenerator loresPattern, hiresPattern;
|
||||
TextPatternGenerator textPattern;
|
||||
|
||||
void init(Timer timer, ubyte[] rom)
|
||||
{
|
||||
assert(textPattern !is null);
|
||||
|
||||
textPattern.signal = this;
|
||||
textPattern.scanner = scanner;
|
||||
textPattern.init(timer);
|
||||
textPattern.initPatterns(rom);
|
||||
loresPattern = new LoresPatternGenerator();
|
||||
hiresPattern = new HiresPatternGenerator();
|
||||
loresPattern.scanner = scanner;
|
||||
hiresPattern.scanner = scanner;
|
||||
}
|
||||
|
||||
void reboot()
|
||||
{
|
||||
textPattern.reboot();
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
if (!scanner.shouldUpdate()) return;
|
||||
|
||||
int stopLine = scanner.currentLine();
|
||||
int stopCol = scanner.currentCol();
|
||||
|
||||
Mode mode = scanner.getMode();
|
||||
bool col80 =
|
||||
((mode == Mode.TEXT) ? col80Switch : (col80Switch && dHGR));
|
||||
bool colorBurst = scanner.checkColorBurst();
|
||||
|
||||
if (nextLine < 192)
|
||||
{
|
||||
update(mode, col80, colorBurst, nextLine, nextCol, stopLine,
|
||||
stopCol);
|
||||
}
|
||||
|
||||
if (stopCol == 64)
|
||||
{
|
||||
nextLine = (stopLine + 1) % 262; // XXX PAL: 312
|
||||
nextCol = 25;
|
||||
}
|
||||
else
|
||||
{
|
||||
nextLine = stopLine;
|
||||
nextCol = stopCol + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void update(Mode mode, bool col80, bool colorBurst, int startLine,
|
||||
int startCol, int stopLine, int stopCol)
|
||||
{
|
||||
PatternGenerator pattern;
|
||||
switch (mode)
|
||||
{
|
||||
case Mode.TEXT:
|
||||
pattern = textPattern;
|
||||
break;
|
||||
case Mode.LORES:
|
||||
pattern = loresPattern;
|
||||
break;
|
||||
case Mode.HIRES:
|
||||
pattern = hiresPattern;
|
||||
break;
|
||||
}
|
||||
|
||||
int lastLine = (stopLine < 192) ? stopLine : 191;
|
||||
int scanStart = (startCol < 25) ? 0 : startCol - 25;
|
||||
int scanEnd = 39;
|
||||
ubyte* bitmapPtr;
|
||||
|
||||
for (int scanLine = startLine; scanLine <= lastLine; ++scanLine)
|
||||
{
|
||||
if (scanLine == stopLine)
|
||||
{
|
||||
if (stopCol < 25) break;
|
||||
scanEnd = stopCol - 25;
|
||||
}
|
||||
|
||||
bitmapPtr = bitmap.ptr + ((scanLine * 80) + (scanStart * 2));
|
||||
|
||||
if (col80)
|
||||
pattern.update80(bitmapPtr, scanLine, scanStart,
|
||||
(scanEnd - scanStart) + 1);
|
||||
else
|
||||
pattern.update(bitmapPtr, scanLine, scanStart,
|
||||
(scanEnd - scanStart) + 1);
|
||||
|
||||
screen.draw(col80, colorBurst,
|
||||
scanStart == 0 ? bitmapPtr: bitmapPtr - 1,
|
||||
scanLine, scanStart, scanEnd);
|
||||
|
||||
scanStart = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void col80SwitchChange(bool newCol80Switch)
|
||||
{
|
||||
if (col80Switch == newCol80Switch) return;
|
||||
update();
|
||||
col80Switch = newCol80Switch;
|
||||
}
|
||||
|
||||
void dHGRChange(bool newdHGR)
|
||||
{
|
||||
if (dHGR == newdHGR) return;
|
||||
update();
|
||||
dHGR = newdHGR;
|
||||
}
|
||||
|
||||
void initSwitches(SoftSwitchPage switches, int slot = -1)
|
||||
{
|
||||
textPattern.initSwitches(switches, slot);
|
||||
}
|
||||
}
|
||||
|
||||
class Signal_II : Signal
|
||||
{
|
||||
void init(Timer timer, ubyte[] rom)
|
||||
{
|
||||
textPattern = new TextPatternGenerator_II();
|
||||
super.init(timer, rom);
|
||||
loresPattern.initPatterns();
|
||||
hiresPattern.initPatterns();
|
||||
}
|
||||
}
|
||||
|
||||
class Signal_IIe : Signal
|
||||
{
|
||||
void init(Timer timer, ubyte[] rom)
|
||||
{
|
||||
textPattern = new TextPatternGenerator_IIe();
|
||||
textPattern.kbdLatch = kbdLatch;
|
||||
super.init(timer, rom);
|
||||
loresPattern.initPatterns(rom);
|
||||
hiresPattern.initPatterns(rom);
|
||||
}
|
||||
|
||||
void reboot()
|
||||
{
|
||||
super.reboot();
|
||||
col80SwitchChange(false);
|
||||
}
|
||||
|
||||
void col80SwitchOff()
|
||||
{
|
||||
col80SwitchChange(false);
|
||||
}
|
||||
|
||||
void col80SwitchOn()
|
||||
{
|
||||
col80SwitchChange(true);
|
||||
}
|
||||
|
||||
ubyte readCol80Switch()
|
||||
{
|
||||
return kbdLatch() | (col80Switch ? 0x80 : 0x00);
|
||||
}
|
||||
|
||||
mixin(InitSwitches("super", [
|
||||
mixin(MakeSwitch([0xC00C], "W", "col80SwitchOff")),
|
||||
mixin(MakeSwitch([0xC00D], "W", "col80SwitchOn")),
|
||||
mixin(MakeSwitch([0xC01F], "R", "readCol80Switch"))
|
||||
]));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user