Initial commit

This commit is contained in:
Ed McCardell 2012-03-13 20:43:29 -04:00
commit 44f39fe332
52 changed files with 11638 additions and 0 deletions

339
COPYING Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
];

View 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
];

View 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
View 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
];

View 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

Binary file not shown.

BIN
src/data/hires_test Normal file

Binary file not shown.

442
src/data/laz_scheme Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

430
src/peripheral/langcard.d Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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"))
]));
}