mirror of
https://github.com/aufflick/Catakig.git
synced 2024-11-26 00:49:16 +00:00
For v2.00b2 release.
This commit is contained in:
parent
1d3ce5eeb0
commit
1195235253
4
English.lproj/MyDocument.nib/info.nib
generated
4
English.lproj/MyDocument.nib/info.nib
generated
@ -3,7 +3,7 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IBDocumentLocation</key>
|
||||
<string>64 24 356 240 0 0 1152 842 </string>
|
||||
<string>65 32 356 240 0 0 1152 842 </string>
|
||||
<key>IBEditorPositions</key>
|
||||
<dict>
|
||||
<key>43</key>
|
||||
@ -16,6 +16,6 @@
|
||||
<key>IBOldestOS</key>
|
||||
<integer>3</integer>
|
||||
<key>IBSystem Version</key>
|
||||
<string>8J135</string>
|
||||
<string>8L127</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
BIN
English.lproj/MyDocument.nib/keyedobjects.nib
generated
BIN
English.lproj/MyDocument.nib/keyedobjects.nib
generated
Binary file not shown.
24
Info.plist
24
Info.plist
@ -6,6 +6,30 @@
|
||||
<string>English</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>dsk</string>
|
||||
<string>do</string>
|
||||
<string>po</string>
|
||||
<string>nyb</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>FloppyDisk</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>5-inch disk</string>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array>
|
||||
<string>A2D5</string>
|
||||
<string>DSK5</string>
|
||||
</array>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>None</string>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<false/>
|
||||
<key>NSPersistentStoreTypeKey</key>
|
||||
<string>Binary</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
|
@ -31,7 +31,13 @@
|
||||
/TurnOn { Style or /Style exch def} def % nFlags --
|
||||
/SpcWidth {(-- ) BlessString stringwidth pop mul} def % nSpaces -- width
|
||||
|
||||
/G {Inch exch idiv exch 0 exch ashow} def % sData nDotsPerHorizInch --
|
||||
/G % sData nDotsPerHorizInch --
|
||||
{
|
||||
Inch exch idiv exch 0 exch
|
||||
% dup 0 127 put dup 1 128 put
|
||||
ashow
|
||||
} def
|
||||
|
||||
/G0 { 60 G} def % sData --
|
||||
/G1 {120 G} def % sData --
|
||||
/G2 {120 G} def % sData --
|
||||
@ -153,7 +159,6 @@
|
||||
|
||||
/setdistillerparams where { pop <<
|
||||
%/CompatibilityLevel 1.4
|
||||
%/PDFSETTINGS /printer
|
||||
%/NeverEmbed [/Courier]
|
||||
%/EmbedAllFonts true
|
||||
/SubsetFonts true
|
||||
@ -167,3 +172,4 @@
|
||||
|
||||
/Ibsen findfont /T0Font get 12 Scale mul scalefont setfont
|
||||
Reset
|
||||
|
||||
|
110
Notes/Read-me-first.html
Normal file
110
Notes/Read-me-first.html
Normal file
@ -0,0 +1,110 @@
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
h3 { text-align: center; background-color: lime; }
|
||||
body { margin: 1em; background-color: white; }
|
||||
</style> </head> <body>
|
||||
|
||||
<p>
|
||||
For anyone browsing Catakig's source code, hoping to make some sense of it, here's a brief summary . . .
|
||||
|
||||
<h3>FILE (DIS-)ORGANIZATION</h3>
|
||||
|
||||
<p>
|
||||
The source code is grouped into three major parts:
|
||||
|
||||
<ol>
|
||||
<li>the MacOS X Cocoa application (in Source/Cocoa)
|
||||
<li>the Apple II emulation library (in Source/LibAppleII)
|
||||
<li>generally useful C and Objective-C stuff, not specific to this project (in Source/Misc)
|
||||
</ol>
|
||||
|
||||
<p>
|
||||
Part 1 depends on parts 2 & 3. Part 2 depends on part 3. Part 1 is allowed to have Cocoa-specific code, whereas the other two are meant to be portable, and should rely only on <i>FoundationKit</i>, <i>AppKit</i>, and standard Unix APIs.
|
||||
<p>
|
||||
File <b>Prefix.pch</b> is the project's pre-compiled header file. Its content is
|
||||
implicitly included by every source file.
|
||||
<p>
|
||||
The <b>LibAppleII</b> emulation library is driven from the outside. (You call it -- it doesn't call you.) The client of the library, the surrounding application, is expected to pass along all relevant user input, to call the 65c02 interpreter, and to call the video frame renderer, whenever these tasks are needed. The library doesn't initiate any action on its own.
|
||||
<p>
|
||||
Documentation of the source code is pretty scant right now. Most methods do have a brief summary at their beginning, and most files have a few comments at the top describing the contents. But there isn't anything right now to document how the whole thing hangs together.
|
||||
|
||||
<h3>DESIGN GOALS</h3>
|
||||
|
||||
<p>
|
||||
Adhere as much as possible to POSIX, OpenGL, and the common subset of Cocoa and GNUStep. Maybe port someday to generic Linux/BSD + GNUStep platforms. OpenAL might be a another good standard, for audio output.
|
||||
<p>
|
||||
Target MacOS X 10.3, and avoid 10.4+ features. (Occasionally review this decision though.) It would be nice to support 10.2 as well, as I was trying to do initially, but it seems time to move on. Note that MacOS X on Intel must always at least version 10.4.
|
||||
<p>
|
||||
For now, don't bother supporting arbitrary Apple II peripherals in arbitrary slot configurations. Rather, aim for a "canonical" Apple II with the same feature set across all models. These features are:
|
||||
|
||||
<ul>
|
||||
<li>a printer, slot #1
|
||||
<li>a Mockingboard sound card, slot #2. (The IIc has a serial port for modems in
|
||||
slot #2, but this can be used to talk to the Mockingboard D. Supporting modems under emulation is probably pointless today anyway.)
|
||||
<li>a ThunderClock-compatible clock card, slot #3?
|
||||
<li>a "Slinky" RAM card, slot #4
|
||||
<li>a pair of ProDOS/SmartPort drives, slot #5
|
||||
<li>a pair of Disk II 5.25-inch drives, slot #6
|
||||
<li> mouse?, slot #7
|
||||
</ul>
|
||||
|
||||
<h3>NAMING CONVENTIONS IN THE SOURCE</h3>
|
||||
|
||||
<p>
|
||||
In general, identifiers are in mixed-case form (e.g. "doItNow") -- except C pre-processor macros, which follow the ancient C tradition of being all uppercase, with words separated by underscores ("DO_IT_NOW").
|
||||
<p>
|
||||
All LibAppleII identifiers in the publicly visible scope, except enum constants, begin with "A2".
|
||||
<p>
|
||||
Enumeration constants begin with "kf" when they're flags (integers having a single 1 bit, e.g. 0x400), "ks" when bit-shift values (0 to 31), "km" when bit-masks (e.g. 0x3FF), and just "k" otherwise. Public enumeration constants also have "A2" in their prefix. Flag and shift enum values are often defined in pairs: e.g. "kfALTZP" and "ksALTZP".
|
||||
<p>
|
||||
Names of object instance variables begin with "m" and are mixed-case ("mFavoriteColor"). This convention seems to go against popular Objective-C practice, but I don't care. It helps me.
|
||||
<p>
|
||||
Methods supplied by the author that don't exist in the standard class
|
||||
libraries have capitalized names. For example: "InputChar" and not
|
||||
"inputChar" -- but on the other hand "keyDown" and not "KeyDown". In other
|
||||
words, lower-cased methods will have some pre-defined purpose in the system
|
||||
libraries, because of class inheritance, whereas the upper-cased methods are entirely
|
||||
new additions. Again, this is a personal convention that helps me.
|
||||
<p>
|
||||
Methods begining with an underscore ( _ ) are considered private to the class,
|
||||
for internal use only. Objective-C has no "private" attribute like C++ does
|
||||
to enforce this behavior however. Any Objective-C message can be sent to
|
||||
any object at any time. But the underscore alerts the reader that the method
|
||||
isn't supposed to be called from just anywhere.
|
||||
|
||||
<h3>CODING HABITS OF THE AUTHOR THAT WILL ANNOY YOU</h3>
|
||||
|
||||
<p>
|
||||
Hard tabs are used in the source, and are expected to be 4 spaces wide.
|
||||
Might switch to soft tabs (all spaces) at some point.
|
||||
<p>
|
||||
The author often exploits the fact that C literal strings are arrays
|
||||
of constant characters, that C characters can also serve as small
|
||||
integers, and that therefore short literal strings make handy little
|
||||
in-line lookup tables of small integers. For example:
|
||||
|
||||
<pre>
|
||||
number_of_one_bits = "\0\1\1\2\1\2\2\3"[n]
|
||||
</pre>
|
||||
|
||||
You're probably entitled to be outraged at this practice.
|
||||
|
||||
<p>
|
||||
Comments ending with "??" or "!!" are considered temporary, and not part
|
||||
of the settled in-line documentation -- if such a thing will ever exist.
|
||||
These annotations usually mark code that is volatile, experimental, or
|
||||
whose need is questionable.
|
||||
<p>
|
||||
There are some C99 (and non-C89) features used pretty frequently: (1) declaring
|
||||
for-loop variables in-line; (2) initializing structure fields by name
|
||||
instead of by position. The first one is pretty well known, but use of the second feature doesn't seem very widespread and might be a surprise to some.
|
||||
<p>
|
||||
The author prefers using "and", "or", and "not" over "&&", "||" and "!".
|
||||
Macros defining these pseudo-keywords are in the header <b>Prefix.pch</b>.
|
||||
|
||||
<p align=right><i>
|
||||
Colin K.<br>Oct. 2006
|
||||
</i></p>
|
||||
|
||||
</body> </html>
|
42
Notes/To-do-Cocoa.txt
Normal file
42
Notes/To-do-Cocoa.txt
Normal file
@ -0,0 +1,42 @@
|
||||
|
||||
==== Preferences ====
|
||||
|
||||
* Let user pick the colors of color video. Or, NTSC vs. PAL.
|
||||
* Checkbox for cassette tape audio output.
|
||||
* Skip lines in monochrome/color video (two separate checkboxes).
|
||||
* Checkbox for Mockingboard (when implemented).
|
||||
* Checkbox for disk drive sound effects (when implemented).
|
||||
* "Start Apple IIs with power already on" checkbox.
|
||||
* Sound volume control in application?
|
||||
* Add "success" and "failure" feedback beeps, with ability to disable.
|
||||
* Mouse-joystick mapping assumes only one screen, the main one.
|
||||
Should instead range over the entire desktop.
|
||||
|
||||
|
||||
==== the Apple II window ====
|
||||
|
||||
* Make dashboard into a drawer? A toolbar? Anyway, let user hide it.
|
||||
* Optionally show annunciator lights in dashboard. Not on IIc though.
|
||||
* Need per-window preferences?
|
||||
* Are we capturing screens in the best way?
|
||||
* Drag & Drop disk slots
|
||||
|
||||
|
||||
==== Miscellany ====
|
||||
|
||||
* Internationalize displayed text.
|
||||
* Add built-in help pages: HTML or PDF
|
||||
* Add sound recording.
|
||||
* Add QuickTime movie recording.
|
||||
* Support real joysticks, via IOKit.
|
||||
* Add more control over paddle buttons.
|
||||
* Use third-party Cocoa kits? OmniGroup's?
|
||||
* Rethink error handling and reporting! Add Log window?
|
||||
* Add built-in PDF/HTML help.
|
||||
* Would Xcode build phases be useful?
|
||||
* Use zlib 1.2.3? nulib?
|
||||
* Add OSX icons for '.rom', '.hdv' files. Others?
|
||||
* Rename the 'MyDocument' class to something more inspired. (This is a
|
||||
relic of the original project stationery.)
|
||||
* Save and restore states. (Open, Save, Revert, etc.)
|
||||
* Quick save and restore?
|
100
Notes/To-do-LibAppleII.txt
Normal file
100
Notes/To-do-LibAppleII.txt
Normal file
@ -0,0 +1,100 @@
|
||||
|
||||
==== Non-functioning Apple II Software (that should work) ====
|
||||
|
||||
* Rescue Raiders? Usually launches, but sometimes re-boots mysteriously.
|
||||
(Could just be the disk image I'm using.)
|
||||
* Stellar 7? Seems okay now, but wasn't working earlier.
|
||||
* Apple Panic, GraForth. Something wrong with our keypress buffering?
|
||||
|
||||
|
||||
==== CPU ====
|
||||
|
||||
* Find quicker Z-flag query expression? (Rethink ZF flag.)
|
||||
* Use rotated opcodes: op<<6 | op>>2 ?
|
||||
* Keep A in a local variable?
|
||||
* Implement old 6502 ADC & SBC behavior (different flags than 65c02)
|
||||
* Video floater bytes not correct yet.
|
||||
* On ][ and ][+, RESET should not affect the language card state.
|
||||
* Keep 'scanLine' in upper bits of 't', instead of extra variable.
|
||||
* Peripheral expansion ROMs not being swapped in correctly.
|
||||
|
||||
|
||||
==== Video ====
|
||||
|
||||
* Review D/HGR rendering. Glitches still apparent?
|
||||
* Support European screen glyphs.
|
||||
* Support European (PAL) color palette.
|
||||
* Implement the "bizarre" (and useless) GR video mode?
|
||||
* Simulate color burst for text in graphics mode.
|
||||
* Implement Videx 80-column card for IIo and IIp models?
|
||||
* Eliminate need to scale array index by 4 when using 'vpix' tables?
|
||||
|
||||
|
||||
==== Audio ====
|
||||
|
||||
* Add Mockingboard emulation.
|
||||
* Add sound effects to printer and disk drive activity.
|
||||
* Support cassette tape input? Probably not worth it.
|
||||
* There are occasional pops in the audio output.
|
||||
|
||||
|
||||
==== Disk Drives & Image Files ====
|
||||
|
||||
* Support BZ- and GZ-compressed disk image files.
|
||||
* Use track size of 6384-6400 bytes instead of 6656?
|
||||
* Infer DO/PO format of 140K disks by examining their contents.
|
||||
* On computer Reset, does the IWM mode register revert to 0?
|
||||
* Update checksum on modified DiskCopy 4 images.
|
||||
* Does IWM mode register change after RESET?
|
||||
* Support FDI disk images? NB2?
|
||||
* Forbid Disk II drives accepting 2IMG-PO disks > 140 KB.
|
||||
* On real Apple II with IWM, verify IWM status register value under all
|
||||
possible drive contents: none, read-only, read-write.
|
||||
* Failure to load a drive should _not_ empty it?
|
||||
* Need to regularize NIB tracks when loading?
|
||||
|
||||
|
||||
==== Printer ====
|
||||
|
||||
* Not processing rubout characters ($7F) yet.
|
||||
* The Flex scanner is not re-entrant, and not thread-safe. Use 're2c'?
|
||||
* Output PS should be friendly to A4 paper sizes, and PDF distilling.
|
||||
* Use PDF-lib library for printing to PDF?
|
||||
* Preferences:
|
||||
LF after CR, yes or no
|
||||
7 or 8 bit data path
|
||||
card DIP switch states
|
||||
page size US Letter or A4
|
||||
line width 40 or 80
|
||||
* Epson emulation:
|
||||
Ibsen font bounding boxes needed
|
||||
Tab stops are not always a constant width.
|
||||
Graphics character set; European character sets.
|
||||
Handle rubout and backspace characters properly.
|
||||
Vertical tabs not handled.
|
||||
|
||||
|
||||
==== Model-specific Features ====
|
||||
|
||||
* The IIc+ doesn't work yet. Worth bothering with?
|
||||
* IIc: provide a 40/80 column switch?
|
||||
* IIc: detect various ROM versions, an act appropriately. In particular,
|
||||
the mouse firmware can be in slot 4 or 7, the number of ROM banks can
|
||||
be either 1 or 2, and expansion RAM might or might not be available.
|
||||
* IIc: reading $C048 should reset the X0/Y0 interrupt flags.
|
||||
|
||||
|
||||
==== Miscellany ====
|
||||
|
||||
* State saving and restoring!
|
||||
* The Apple ][ and ][+ are wasting 64KB for aux. RAM that's never used.
|
||||
* Add Z80 and CP/M support?
|
||||
* Emulate RAMWorks card? ThunderClock?
|
||||
* Use ObjC exceptions instead of error codes? (Requires MacOS 10.3+)
|
||||
How about using NSError?
|
||||
* Use NSFileHandles or NSStreams instead of Unix FDs and FILE pointers?
|
||||
* Add Apple II mouse support.
|
||||
* Implement the Parallel Interface Card? It's much simpler than the SSC.
|
||||
* Eliminate non-POSIX calls?
|
||||
* Have different models inherit from a base class?
|
||||
* Save and restore NSUserDefaults for the library.
|
48
Notes/XCode-project.txt
Normal file
48
Notes/XCode-project.txt
Normal file
@ -0,0 +1,48 @@
|
||||
|
||||
========== BUILDING CATAKIG IN XCODE 2, FOR MACOS X ==========
|
||||
|
||||
The information here should be helpful on those occasions when the XCode
|
||||
project needs to be re-constructed -- either because Apple comes out with
|
||||
a new, radically different version of their IDE (as they've done twice
|
||||
on my watch), or because the project file gets lost or damaged. This
|
||||
information should also be helpful for writing a Unix "Makefile" or
|
||||
"autoconf" script, should anyone want to do that at some point.
|
||||
-- CK
|
||||
|
||||
|
||||
==== Files in the project ====
|
||||
|
||||
For source code, everything in folder "Source". Just drag and drop the
|
||||
whole folder into the project.
|
||||
|
||||
For Resources, everything in folder "More-Resources". Again, just drag and
|
||||
drop into the project's "Resources" group.
|
||||
|
||||
The '.xcconfig' files are used to set target configuration options, instead of XCode's dialog interface to them. File 'debug.xcconfig' is for the Debug target, file 'release.xcconfig' is for the Release target, and both incorporate 'common.xcconfig'.
|
||||
|
||||
|
||||
==== Linked frameworks ====
|
||||
|
||||
* Cocoa, OpenGL, AudioUnit, CoreAudio
|
||||
* Carbon (needed only for the 'SetSystemUIMode' function)
|
||||
|
||||
|
||||
==== NIB notes ====
|
||||
|
||||
* The single AppController object is the delegate for both the main
|
||||
NSApplication and NSMenu objects.
|
||||
|
||||
* The main application object is actually an instance of 'MyApplication',
|
||||
which subclasses NSApplication. Subclassing is done so that we
|
||||
can override '-sendEvent'.
|
||||
|
||||
* An Apple II window's ScreenView is also its initial (and permanent)
|
||||
First Responder. The Apple II object is the next responder after
|
||||
that, then the NSWindow and the usual responder chain.
|
||||
|
||||
* Each Apple II NSDocument is the delegate of its window.
|
||||
|
||||
* Menu command "New" was renamed "New Apple II" -- and, instead of
|
||||
sending -newDocument to First Responder, sends this message to the
|
||||
AppController object. (This is so we can put up the "New Apple II"
|
||||
panel first.)
|
9
Source/Cocoa/AboutPanel.h
Normal file
9
Source/Cocoa/AboutPanel.h
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
@interface AboutPanel : NSPanel
|
||||
{
|
||||
IBOutlet NSControl* mVersion;
|
||||
IBOutlet NSTextField* mCursorCover;
|
||||
|
||||
NSTimer* mStrober;
|
||||
}
|
||||
@end
|
76
Source/Cocoa/AboutPanel.m
Normal file
76
Source/Cocoa/AboutPanel.m
Normal file
@ -0,0 +1,76 @@
|
||||
/* class AboutPanel
|
||||
|
||||
Catakig's About box. Only one instance exists at runtime.
|
||||
*/
|
||||
#import "Catakig-Cocoa.h"
|
||||
#import "AboutPanel.h"
|
||||
#import "ScreenView.h"
|
||||
|
||||
@implementation AboutPanel
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)_Strobe:(NSTimer*)timer
|
||||
{
|
||||
static int counter = 0;
|
||||
|
||||
if (--counter > 0)
|
||||
[G.activeScreen setNeedsDisplay:YES];
|
||||
else
|
||||
{
|
||||
[G.activeScreen Flash];
|
||||
counter = 7;
|
||||
|
||||
if ([self isKeyWindow])
|
||||
{
|
||||
[mCursorCover setDrawsBackground:
|
||||
[mCursorCover drawsBackground] ^ 1];
|
||||
[mCursorCover setNeedsDisplay];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
// Set version string in the About box to value of CFBundleVersion
|
||||
// entry in the Info.plist dictionary.
|
||||
|
||||
NSString* vstr;
|
||||
|
||||
vstr = [[G.bundle infoDictionary] objectForKey:@"CFBundleVersion"];
|
||||
if (vstr != nil)
|
||||
[mVersion setStringValue:vstr];
|
||||
|
||||
// Turn on the cursor flasher.
|
||||
|
||||
mStrober = [NSTimer scheduledTimerWithTimeInterval: 1./30
|
||||
target: self
|
||||
selector: @selector(_Strobe:)
|
||||
userInfo: nil
|
||||
repeats: YES ];
|
||||
|
||||
// [self setDelegate:self];
|
||||
if (G.prefs.firstLaunch)
|
||||
[self makeKeyAndOrderFront:self];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
#if 0
|
||||
- (void)close
|
||||
{
|
||||
[mStrober invalidate];
|
||||
}
|
||||
#endif
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)makeKeyAndOrderFront:(id)sender
|
||||
{
|
||||
if (not [self isVisible])
|
||||
[self center];
|
||||
|
||||
[super makeKeyAndOrderFront:sender];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
13
Source/Cocoa/AppController.h
Normal file
13
Source/Cocoa/AppController.h
Normal file
@ -0,0 +1,13 @@
|
||||
|
||||
@interface AppController : NSObject
|
||||
{
|
||||
//---------------- "New Apple II" dialog ----------------
|
||||
|
||||
IBOutlet NSPanel* mNewA2Panel;
|
||||
IBOutlet NSMatrix* mNewA2Model;
|
||||
IBOutlet NSControl* mNewA2ExtraRAM;
|
||||
}
|
||||
|
||||
//- (IBAction) newDocument:(id)sender;
|
||||
|
||||
@end
|
118
Source/Cocoa/AppController.m
Normal file
118
Source/Cocoa/AppController.m
Normal file
@ -0,0 +1,118 @@
|
||||
/* class AppController
|
||||
|
||||
A dispatcher for many high-level application events. Only one instance
|
||||
exists at runtime.
|
||||
*/
|
||||
#import "Catakig-Cocoa.h"
|
||||
#import "AppController.h"
|
||||
#import "ScreenView.h"
|
||||
|
||||
@implementation AppController
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
OSStatus sts;
|
||||
|
||||
// Need these retains??
|
||||
G.bundle = [[NSBundle mainBundle] retain];
|
||||
G.docMgr = [[NSDocumentController sharedDocumentController] retain];
|
||||
G.fileMgr = [[NSFileManager defaultManager] retain];
|
||||
G.helpMgr = [[NSHelpManager sharedHelpManager] retain];
|
||||
// G.null = [[NSNull null] retain];
|
||||
G.pboard = [[NSPasteboard generalPasteboard] retain];
|
||||
G.workspace = [[NSWorkspace sharedWorkspace] retain];
|
||||
|
||||
SetMouseRange();
|
||||
|
||||
if (noErr != (sts = AU_Open(&G.audioUnit)))
|
||||
{
|
||||
FatalErrorAlert(@"Cannot initialize audio",
|
||||
@"Error from AU_Open.");
|
||||
}
|
||||
|
||||
if (NO) //!!
|
||||
{
|
||||
NSArray* arr;
|
||||
|
||||
arr = [G.fileMgr directoryContentsAtPath:@"/System/Library/Sounds"];
|
||||
NSLog(@"Found sounds: %@", arr);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
#if 0
|
||||
- (void)applicationDidFinishLaunching:(NSNotification*)note
|
||||
{/*
|
||||
"Sent by the default notification center after the application has been
|
||||
launched and initialized but before it has received its first event."
|
||||
*/
|
||||
}
|
||||
#endif
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (NSApplicationTerminateReply)
|
||||
applicationShouldTerminate:(NSApplication*)app
|
||||
{
|
||||
AU_Close(&G.audioUnit);
|
||||
[ScreenView FullScreenOff];
|
||||
|
||||
return NSTerminateNow;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (BOOL)applicationShouldOpenUntitledFile:(NSApplication*)sender
|
||||
{/*
|
||||
Returns whether the application should automatically create a new
|
||||
document after launching. (Default behavior is YES.)
|
||||
*/
|
||||
return NO;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)applicationDidChangeScreenParameters:(NSNotification*)note
|
||||
{/*
|
||||
Responds to changes in screen configuration: either resolutions, or
|
||||
relative positions of multiple screens.
|
||||
*/
|
||||
SetMouseRange();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (IBAction)newDocument:(id)sender
|
||||
{/*
|
||||
Called when the user invokes the "New" menu command. (Normally the
|
||||
global DocumentController gets this message first.)
|
||||
*/
|
||||
NSEnumerator* e = [[mNewA2Model cells] objectEnumerator];
|
||||
NSColor* warnColor = [NSColor colorWithDeviceHue:0.
|
||||
saturation:0.4 brightness:1. alpha:1.];
|
||||
|
||||
// Scan for Apple II ROMs.
|
||||
|
||||
[A2Computer ScanDirectoryForROM:nil];
|
||||
|
||||
for (NSButtonCell* cell; (cell = [e nextObject]);)
|
||||
{
|
||||
BOOL hasROM = [A2Computer ModelHasROM:[cell tag]];
|
||||
|
||||
[cell setBackgroundColor:(hasROM? nil : warnColor)];
|
||||
}
|
||||
|
||||
// Run the "New Apple II" dialog.
|
||||
|
||||
if ([mNewA2Panel RunModal])
|
||||
{
|
||||
A2G.defaultModel = [mNewA2Model selectedTag];
|
||||
A2G.defaultExtraRAM = [mNewA2ExtraRAM selectedTag];
|
||||
|
||||
[G.docMgr newDocument:sender];
|
||||
// [G.docMgr openUntitledDocumentOfType ... ];
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
67
Source/Cocoa/Catakig-Cocoa.h
Normal file
67
Source/Cocoa/Catakig-Cocoa.h
Normal file
@ -0,0 +1,67 @@
|
||||
/* Catakig-Cocoa.h
|
||||
|
||||
Primary header for source files of the MacOS X application.
|
||||
Not used by lower-level libraries.
|
||||
*/
|
||||
#import "MyUtils.h"
|
||||
#import "LibAppleII.h"
|
||||
|
||||
@class ScreenView;
|
||||
|
||||
enum
|
||||
{
|
||||
kJoyMouse = 0, // mechanisms for paddle & joystick control
|
||||
kJoyKeypad,
|
||||
// kJoyCenteringKeypad, kJoyReal,
|
||||
};
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int32_t monochromeHue; // low 24 bits: R, G, B
|
||||
BOOL beepsOn,
|
||||
keepBackupFiles,
|
||||
firstLaunch;
|
||||
uint8_t joystickControl; // = kJoyMouse, etc.
|
||||
|
||||
#if 0
|
||||
BOOL monochromeGlow;
|
||||
BOOL showHelpTips;
|
||||
BOOL moreAlerts;
|
||||
uint8_t frameRate; // 20, 30, or 60
|
||||
uint8_t skipLines;
|
||||
#endif
|
||||
|
||||
} Prefs;
|
||||
|
||||
|
||||
extern struct CatakigGlobals
|
||||
{
|
||||
NSBundle* bundle;
|
||||
NSDocumentController* docMgr;
|
||||
NSFileManager* fileMgr;
|
||||
NSHelpManager* helpMgr;
|
||||
// NSNull* null;
|
||||
NSPasteboard* pboard;
|
||||
NSWorkspace* workspace;
|
||||
|
||||
Prefs prefs;
|
||||
AudioUnit audioUnit;
|
||||
// BOOL inFullScreenMode;
|
||||
ScreenView* activeScreen;
|
||||
} G;
|
||||
|
||||
|
||||
void BeepFor(BOOL);
|
||||
void ErrorAlert(NSWindow*, NSString*, NSString*);
|
||||
void FatalErrorAlert(NSString*, NSString*);
|
||||
void SetMouseRange(void);
|
||||
|
||||
OSStatus AU_Open(AudioUnit*);
|
||||
OSStatus AU_SetBufferFrameSize(AudioUnit, UInt32);
|
||||
void AU_Close(AudioUnit*);
|
||||
|
||||
BOOL GL_CheckExtension(const char* name);
|
||||
void GL_ClearBothBuffers(void);
|
||||
void GL_PrepareViewport(int viewWidth, int viewHeight);
|
||||
void GL_PreparePalette(void);
|
6
Source/Cocoa/MyApplication.h
Normal file
6
Source/Cocoa/MyApplication.h
Normal file
@ -0,0 +1,6 @@
|
||||
//#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface MyApplication : NSApplication
|
||||
{
|
||||
}
|
||||
@end
|
38
Source/Cocoa/MyApplication.m
Normal file
38
Source/Cocoa/MyApplication.m
Normal file
@ -0,0 +1,38 @@
|
||||
/* class MyApplication
|
||||
*/
|
||||
#import "MyApplication.h"
|
||||
#import "Catakig-Cocoa.h"
|
||||
|
||||
@implementation MyApplication
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)sendEvent:(NSEvent*)event
|
||||
{
|
||||
uint32_t mods = [event modifierFlags];
|
||||
unsigned b = 0;
|
||||
|
||||
if (mods & NSCommandKeyMask)
|
||||
b |= kfA2KeyOpenApple;
|
||||
if (mods & NSAlternateKeyMask)
|
||||
b |= kfA2KeySolidApple;
|
||||
if (mods & NSShiftKeyMask)
|
||||
b |= kfA2KeyShift;
|
||||
A2G.buttons = b;
|
||||
|
||||
if (G.prefs.joystickControl == kJoyMouse)
|
||||
[A2Computer InputPaddlesByMouse];
|
||||
|
||||
#if 0
|
||||
if (mods & NSControlKeyMask)
|
||||
{
|
||||
NSPoint mloc = [NSEvent mouseLocation];
|
||||
NSLog(@"mouse loc = %d, %d", (int)mloc.x, (int)mloc.y);
|
||||
NSBeep();
|
||||
}
|
||||
#endif
|
||||
|
||||
[super sendEvent:event];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
48
Source/Cocoa/MyDocument.h
Normal file
48
Source/Cocoa/MyDocument.h
Normal file
@ -0,0 +1,48 @@
|
||||
|
||||
@class A2Computer, ScreenView, IndicatorLight;
|
||||
|
||||
@interface MyDocument : NSDocument <NSCoding>
|
||||
{
|
||||
IBOutlet A2Computer* mA2;
|
||||
IBOutlet ScreenView* mScreen;
|
||||
IBOutlet IndicatorLight* mSpeedLight;
|
||||
IBOutlet NSTextField* mModelEmblem;
|
||||
IBOutlet NSTextField* mDDrive0;
|
||||
IBOutlet NSTextField* mDDrive1;
|
||||
|
||||
IBOutlet NSView* mSaveImageView;
|
||||
IBOutlet NSControl* mSaveImageTypes;
|
||||
IBOutlet NSControl* mSaveImageAddSuffix;
|
||||
|
||||
IBOutlet NSView* mPrSessionView;
|
||||
IBOutlet NSControl* mPrSessionFilter;
|
||||
IBOutlet NSTextField* mPrSessionSize;
|
||||
IBOutlet NSControl* mPrSessionAddSuffix;
|
||||
|
||||
int mRunState; // low 2 bits: speed; higher bits: pause level
|
||||
int mFileFilter;
|
||||
}
|
||||
|
||||
+ (void) AllNeedDisplay;
|
||||
|
||||
@end
|
||||
|
||||
@interface MyDocument (Audio)
|
||||
|
||||
- (void) Pause;
|
||||
- (void) Unpause;
|
||||
- (BOOL) IsRunning;
|
||||
- (IBAction) HitSpeedControl:(id)sender;
|
||||
|
||||
@end
|
||||
|
||||
@interface MyDocument (Actions)
|
||||
|
||||
- (IBAction) ClearPrintSession:(id)sender;
|
||||
- (IBAction) CopyScreenImage:(id)sender;
|
||||
- (IBAction) PasteScreenText:(id)sender;
|
||||
- (IBAction) CopyScreenText:(id)sender;
|
||||
- (IBAction) SaveScreenImage:(id)sender;
|
||||
- (IBAction) HitDiskDrive:(id)sender;
|
||||
|
||||
@end
|
248
Source/Cocoa/MyDocument/Actions.m
Normal file
248
Source/Cocoa/MyDocument/Actions.m
Normal file
@ -0,0 +1,248 @@
|
||||
#import "Catakig-Cocoa.h"
|
||||
#import "MyDocument.h"
|
||||
|
||||
@implementation MyDocument (Actions)
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (IBAction)ClearPrintSession:(id)sender
|
||||
{/*
|
||||
Clears this Apple II's accumulated print session.
|
||||
*/
|
||||
[mA2 ClearPrintSession];
|
||||
BeepFor(YES);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (IBAction)CopyScreenImage:(id)sender
|
||||
{/*
|
||||
Copies this screen's content to the general pasteboard as a TIFF
|
||||
image.
|
||||
*/
|
||||
NSData* data;
|
||||
|
||||
[self Pause];
|
||||
data = [[mScreen ReadPixels]
|
||||
representationUsingType:NSTIFFFileType properties:nil ];
|
||||
[G.pboard SetData:data forType:NSTIFFPboardType];
|
||||
[self Unpause];
|
||||
BeepFor(YES);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (IBAction)PasteScreenText:(id)sender
|
||||
{/*
|
||||
Enters the pasteboard's text string (if one exists) into this Apple II,
|
||||
as if all the characters had been typed.
|
||||
*/
|
||||
NSString* str = [G.pboard GetString];
|
||||
|
||||
if (str)
|
||||
{
|
||||
[self Pause];
|
||||
[mA2 InputChars:str];
|
||||
[self Unpause];
|
||||
}
|
||||
else
|
||||
BeepFor(NO);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (IBAction)CopyScreenText:(id)sender
|
||||
{/*
|
||||
Copies the Apple II's text screen content (visible or not) to the
|
||||
general pasteboard as one giant string.
|
||||
*/
|
||||
NSString* str;
|
||||
|
||||
[self Pause];
|
||||
str = [mA2 TextScreenAsString:YES];
|
||||
[self Unpause];
|
||||
[G.pboard SetString:str];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (IBAction)SaveScreenImage:(id)sender
|
||||
{/*
|
||||
Saves this Apple II's screen content to an image file of the user's
|
||||
choosing.
|
||||
*/
|
||||
NSSavePanel* panel = [NSSavePanel savePanel];
|
||||
|
||||
// [mSaveImageView retain]; // need retain??
|
||||
|
||||
[panel setMessage:@"Save Screen Image to File"];
|
||||
[panel setAccessoryView:mSaveImageView]; // need retain??
|
||||
// [panel setCanSelectHiddenExtension:YES];
|
||||
|
||||
[panel beginSheetForDirectory:nil
|
||||
file: nil // @"screen"
|
||||
modalForWindow: [mScreen window]
|
||||
modalDelegate: self
|
||||
didEndSelector: @selector(_SaveScreenImage2:resp:sender:)
|
||||
contextInfo: sender ];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)_SaveScreenImage2:(NSSavePanel*)panel
|
||||
resp:(int)userResponse sender:(id)sender
|
||||
{
|
||||
if (userResponse != NSOKButton)
|
||||
return;
|
||||
|
||||
NSData* data;
|
||||
NSString* fpath = [panel filename];
|
||||
int fileType = [mSaveImageTypes selectedTag];
|
||||
NSString* extensions[/*NSBitmapImageFileType*/] =
|
||||
{ @"tiff", @"bmp", @"gif", @"jpeg", @"png" };
|
||||
|
||||
if ([mSaveImageAddSuffix intValue] == NSOnState)
|
||||
fpath = [fpath stringByAppendingPathExtension:
|
||||
extensions[fileType]];
|
||||
|
||||
data = [[mScreen ReadPixels]
|
||||
representationUsingType:fileType properties:nil];
|
||||
if (data == nil)
|
||||
{
|
||||
ErrorAlert([mScreen window],
|
||||
@"Image can't be created.",
|
||||
@"Allocation problem??");
|
||||
return;
|
||||
}
|
||||
|
||||
if (not [data writeToFile:fpath atomically:NO])
|
||||
ErrorAlert([mScreen window],
|
||||
@"Sorry, cannot save the image file.",
|
||||
@"Check that folder write permissions and available disk "
|
||||
"space are sufficient.");
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (IBAction)printDocument:(id)sender
|
||||
{
|
||||
NSSavePanel* panel = [NSSavePanel savePanel];
|
||||
|
||||
// [mPrSessionView retain];
|
||||
[mPrSessionSize setStringValue:
|
||||
[NSString stringWithFormat:@"%lu bytes",
|
||||
[mA2 SizeOfPrintSession] ]];
|
||||
|
||||
[panel setAccessoryView:mPrSessionView];
|
||||
[panel setMessage:@"Save Print Session to File"];
|
||||
// [panel setTitle:@"the title"];
|
||||
|
||||
[panel beginSheetForDirectory:nil
|
||||
file: nil
|
||||
modalForWindow: [mScreen window]
|
||||
modalDelegate: self
|
||||
didEndSelector: @selector(_printDocument2:resp:sender:)
|
||||
contextInfo: sender ];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)_printDocument2:(NSSavePanel*)panel
|
||||
resp:(int)userResponse sender:(id)sender
|
||||
{
|
||||
if (userResponse != NSOKButton)
|
||||
return;
|
||||
|
||||
NSString* fpath = [panel filename];
|
||||
int filter = [mPrSessionFilter selectedTag];
|
||||
|
||||
if ([mA2 SavePrintSessionAs:filter toFile:fpath])
|
||||
{
|
||||
BeepFor(YES);
|
||||
return;
|
||||
}
|
||||
|
||||
BeepFor(NO); // and alert!!
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (IBAction)HitDiskDrive:(id)sender
|
||||
{/*
|
||||
Called when user invokes "Load" or "Unload" on a disk drive.
|
||||
*/
|
||||
int index = abs([sender tag]) - 1;
|
||||
id<A2PrDiskDrive> ddrive = [mA2 DiskDrive:index];
|
||||
NSControl* dname = (&mDDrive0)[index];
|
||||
|
||||
if ([sender tag] < 0) // then unload drive and return
|
||||
{
|
||||
[ddrive Unload];
|
||||
[dname setStringValue:@""];
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
NSOpenPanel* panel = [NSOpenPanel openPanel];
|
||||
NSString* dirStart = nil;
|
||||
NSString* headers[/*drive index*/] = {
|
||||
@"Load Floppy Drive #1",
|
||||
@"Load Floppy Drive #2" };
|
||||
|
||||
mFileFilter = 1; // will filter disk image file names
|
||||
// dirStart = [[G.bundle bundlePath]
|
||||
// stringByAppendingPathComponent:@"../Disks"];
|
||||
|
||||
[panel setDelegate:self];
|
||||
[panel setAllowsMultipleSelection:NO];
|
||||
[panel setMessage:headers[index]];
|
||||
// [panel setPrompt:@"Load"]; //??
|
||||
// [panel setNameFieldLabel:@"Label"];
|
||||
|
||||
[panel beginSheetForDirectory:dirStart
|
||||
file: nil
|
||||
types: nil // (NSArray*)fileTypes
|
||||
modalForWindow: [mScreen window]
|
||||
modalDelegate: self
|
||||
didEndSelector: @selector(_HitDiskDrive2:resp:sender:)
|
||||
contextInfo: sender ];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)_HitDiskDrive2:(NSOpenPanel*)panel
|
||||
resp:(int)userResponse sender:(id)sender
|
||||
{
|
||||
mFileFilter = 0;
|
||||
if (userResponse != NSOKButton)
|
||||
return;
|
||||
|
||||
int index = abs([sender tag]) - 1;
|
||||
id<A2PrDiskDrive> ddrive = [mA2 DiskDrive:index];
|
||||
NSTextField* dname = (&mDDrive0)[index];
|
||||
|
||||
if ([ddrive Load:[panel filename]])
|
||||
{
|
||||
[dname setTextColor:( [ddrive Content] == kA2DiskReadOnly?
|
||||
[NSColor yellowColor] : [NSColor greenColor] )];
|
||||
[dname setStringValue:[ddrive Label]];
|
||||
return;
|
||||
}
|
||||
|
||||
[dname setStringValue:@""];
|
||||
ErrorAlert([mScreen window],
|
||||
@"Failed to load disk!",
|
||||
@"The chosen disk image does not seem to be in a valid format.");
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (BOOL)panel:(id)panel shouldShowFilename:(NSString*)path
|
||||
{
|
||||
if (mFileFilter == 0) // then no filtering to be done
|
||||
return YES;
|
||||
|
||||
return [A2Computer ShouldShowDiskFilename:path];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
292
Source/Cocoa/MyDocument/Audio.m
Normal file
292
Source/Cocoa/MyDocument/Audio.m
Normal file
@ -0,0 +1,292 @@
|
||||
#import "Catakig-Cocoa.h"
|
||||
#import "MyDocument.h"
|
||||
#import "ScreenView.h"
|
||||
#import "IndicatorLight.h"
|
||||
|
||||
@implementation MyDocument (Audio)
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
enum
|
||||
{
|
||||
kSampleRate = 22050,
|
||||
kNumSpeeds = 4,
|
||||
kSpeedMask = kNumSpeeds - 1,
|
||||
kBytesPerChannel = 1,
|
||||
kUnsigned = YES,
|
||||
kFormatFlags = 0,
|
||||
|
||||
#if 0
|
||||
kFormatFlags2 = kAudioFormatFlagIsSignedInteger
|
||||
#if __BIG_ENDIAN__
|
||||
| kAudioFormatFlagIsBigEndian
|
||||
#endif
|
||||
| kAudioFormatFlagIsNonInterleaved
|
||||
| kAudioFormatFlagIsPacked,
|
||||
#endif
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
#if 0
|
||||
static OSStatus InputProc0(
|
||||
ScreenView* screen,
|
||||
AudioUnitRenderActionFlags* ioActionFlags,
|
||||
const AudioTimeStamp* timeStamp,
|
||||
UInt32 busNumber,
|
||||
UInt32 nFrames,
|
||||
AudioBufferList* ioData)
|
||||
{
|
||||
*ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;
|
||||
memset(ioData->mBuffers[0].mData, 0x80, nFrames);
|
||||
return noErr;
|
||||
}
|
||||
#endif
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static OSStatus InputProc12(
|
||||
ScreenView* screen,
|
||||
AudioUnitRenderActionFlags* ioActionFlags,
|
||||
const AudioTimeStamp* timeStamp,
|
||||
UInt32 busNumber,
|
||||
UInt32 nFrames,
|
||||
AudioBufferList* ioData)
|
||||
{/*
|
||||
Called during normal-speed and double-speed emulation.
|
||||
*/
|
||||
uint8_t* data = ioData->mBuffers[0].mData;
|
||||
#if 0
|
||||
static BOOL printed;
|
||||
|
||||
if (not printed)
|
||||
{
|
||||
printed = YES;
|
||||
NSLog(@"%d %d %ld %ld",
|
||||
ioData->mNumberBuffers,
|
||||
ioData->mBuffers[0].mNumberChannels,
|
||||
ioData->mBuffers[0].mDataByteSize,
|
||||
numberFrames);
|
||||
}
|
||||
#endif
|
||||
|
||||
screen->mRunForOneStep(screen->mA2, nil, data);
|
||||
|
||||
#if 0
|
||||
if (nFrames == 368)
|
||||
{
|
||||
for (int i = 91; --i >= 0;)
|
||||
{
|
||||
(data+276)[i] = (data+273)[i];
|
||||
(data+184)[i] = (data+182)[i];
|
||||
(data+ 92)[i] = (data+ 91)[i];
|
||||
data[183] = data[182];
|
||||
data[275] = data[274];
|
||||
data[367] = data[366];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static OSStatus InputProc3(
|
||||
ScreenView* screen,
|
||||
AudioUnitRenderActionFlags* ioActionFlags,
|
||||
const AudioTimeStamp* timeStamp,
|
||||
UInt32 busNumber,
|
||||
UInt32 nFrames,
|
||||
AudioBufferList* ioData)
|
||||
{/*
|
||||
Called during 6x speed emulation. The playback rate is double the
|
||||
norm, and we call the Apple II emulator 3 times in this callback.
|
||||
*/
|
||||
A2Computer* a2 = screen->mA2;
|
||||
uint8_t* data = ioData->mBuffers[0].mData;
|
||||
|
||||
screen->mRunForOneStep(a2, nil, data);
|
||||
screen->mRunForOneStep(a2, nil, data);
|
||||
screen->mRunForOneStep(a2, nil, data);
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static struct
|
||||
{
|
||||
void* inputProc;
|
||||
UInt32 sampleRate,
|
||||
bufFrameSize;
|
||||
}
|
||||
gSpeedInfo[kNumSpeeds] =
|
||||
{
|
||||
{0},
|
||||
{InputProc12, kSampleRate, kA2SamplesPerStep*2},
|
||||
{InputProc12, kSampleRate*2, kA2SamplesPerStep},
|
||||
{InputProc3, kSampleRate*2, kA2SamplesPerStep},
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (OSStatus)_PrepareAudioUnit:(int)speed
|
||||
{
|
||||
AURenderCallbackStruct input =
|
||||
{
|
||||
.inputProcRefCon = mScreen,
|
||||
.inputProc = (AURenderCallback)
|
||||
(gSpeedInfo[speed].inputProc),
|
||||
};
|
||||
AudioStreamBasicDescription format =
|
||||
{
|
||||
.mSampleRate = gSpeedInfo[speed].sampleRate,
|
||||
.mFormatID = kAudioFormatLinearPCM,
|
||||
.mFormatFlags = kFormatFlags,
|
||||
.mFramesPerPacket = 1, // must be 1 for uncompressed data
|
||||
.mChannelsPerFrame = 1, // or 2 for stereo??
|
||||
.mBitsPerChannel = kBytesPerChannel * 8,
|
||||
.mBytesPerFrame = kBytesPerChannel,
|
||||
.mBytesPerPacket = kBytesPerChannel,
|
||||
};
|
||||
OSStatus sts;
|
||||
|
||||
sts = AU_SetBufferFrameSize(G.audioUnit,
|
||||
gSpeedInfo[speed].bufFrameSize);
|
||||
if (sts != noErr)
|
||||
return sts;
|
||||
|
||||
sts = AudioUnitSetProperty(G.audioUnit,
|
||||
kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
|
||||
0, &format, sizeof(format));
|
||||
if (sts != noErr)
|
||||
return sts;
|
||||
|
||||
sts = AudioUnitSetProperty(G.audioUnit,
|
||||
kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
|
||||
0, &input, sizeof(input));
|
||||
if (sts != noErr)
|
||||
return sts;
|
||||
|
||||
// kAudioUnitProperty_MaximumFramesPerSlice (UInt32)
|
||||
// kAudioUnitProperty_SetExternalBuffer (AudioUnitExternalBuffer)
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)_SetRunState:(int)newState
|
||||
{/*
|
||||
Sets the run-state of this document to a new value. The run state is
|
||||
an integer: a combination of the user-selected emulation speed (lower
|
||||
2 bits) and the pause level (remaining upper bits). Emulation occurs
|
||||
only when the run-state value is greater than zero.
|
||||
*/
|
||||
int speed = newState & kSpeedMask;
|
||||
OSStatus sts;
|
||||
|
||||
[mSpeedLight setIntValue:speed];
|
||||
sts = AudioOutputUnitStop(G.audioUnit);
|
||||
|
||||
if ((mRunState = newState) > 0)
|
||||
{
|
||||
sts = [self _PrepareAudioUnit:speed];
|
||||
sts = AudioOutputUnitStart(G.audioUnit);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
NSWindow* mainWindow = [mScreen window];
|
||||
|
||||
// NSLog(@"doc window key? %c", "ny"[[mainWindow isKeyWindow]]); //!!
|
||||
|
||||
[mModelEmblem setStringValue:[mA2 ModelName]];
|
||||
[self setHasUndoManager:NO];
|
||||
[self _SetRunState:(1 - kNumSpeeds)];
|
||||
|
||||
[mScreen setNextResponder:mA2];
|
||||
[mA2 setNextResponder:mainWindow];
|
||||
|
||||
[mainWindow useOptimizedDrawing:YES];
|
||||
// [mainWindow setBackgroundColor:[NSColor blackColor]];
|
||||
// [mainWindow setAcceptsMouseMovedEvents:YES];
|
||||
// [mainWindow setResizeIncrements:NSMakeSize(0, 100)];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (IBAction)HitSpeedControl:(id)sender
|
||||
{/*
|
||||
Responds to the user invoking one of the speed control commands.
|
||||
*/
|
||||
[self _SetRunState:( mRunState & ~kSpeedMask | [sender tag] )];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)windowDidResignKey:(NSNotification*)note
|
||||
{
|
||||
G.activeScreen = nil;
|
||||
[self Pause];
|
||||
[ScreenView FullScreenOff];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)windowDidBecomeKey:(NSNotification*)note
|
||||
{
|
||||
[self Unpause];
|
||||
G.activeScreen = mScreen;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)windowWillMiniaturize:(NSNotification*)note
|
||||
{/*
|
||||
Called when this window is about to be miniturized and put in the Dock.
|
||||
Here we make sure that the window's dock image looks like its content.
|
||||
We must do this ourselves because NSOpenGLViews don't co-operate with
|
||||
Quartz.
|
||||
|
||||
Calling '-setOpaque' is required to make the Quartz underlay and the
|
||||
window shadow appear correctly. We restore the opaque-ness property
|
||||
to YES in '-windowDidMiniaturize'.
|
||||
*/
|
||||
NSWindow* window = [note object];
|
||||
|
||||
[self Pause];
|
||||
[mScreen PrepareToMiniaturize];
|
||||
[window setOpaque:NO];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)Pause
|
||||
{ [self _SetRunState:(mRunState - kNumSpeeds)]; }
|
||||
|
||||
- (void)Unpause
|
||||
{ if (mRunState < 0) [self _SetRunState:(mRunState + kNumSpeeds)]; }
|
||||
|
||||
- (BOOL)IsRunning
|
||||
{ return mRunState > 0; }
|
||||
|
||||
- (void)windowWillBeginSheet:(NSNotification*)note
|
||||
{ [self Pause]; }
|
||||
|
||||
- (void)windowDidEndSheet:(NSNotification*)note
|
||||
{ [self Unpause]; }
|
||||
|
||||
- (void)windowWillClose:(NSNotification*)note
|
||||
{ [self windowDidResignKey:note]; }
|
||||
|
||||
- (void)windowDidMiniaturize:(NSNotification*)note
|
||||
{ [[note object] setOpaque:YES]; [self Unpause]; }
|
||||
// see '-windowWillMiniaturize'
|
||||
|
||||
- (BOOL)keepBackupFile
|
||||
{ return G.prefs.keepBackupFiles; }
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
129
Source/Cocoa/MyDocument/MyDocument.m
Normal file
129
Source/Cocoa/MyDocument/MyDocument.m
Normal file
@ -0,0 +1,129 @@
|
||||
#import "Catakig-Cocoa.h"
|
||||
#import "MyDocument.h"
|
||||
|
||||
enum
|
||||
{
|
||||
kDocCreatorCode = 'Ctkg',
|
||||
kDocTypeCode = 'A2st',
|
||||
};
|
||||
|
||||
@implementation MyDocument
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
+ (void)AllNeedDisplay
|
||||
{/*
|
||||
Requests a redraw of all Apple II screens.
|
||||
*/
|
||||
NSEnumerator* e = [[G.docMgr documents] objectEnumerator];
|
||||
MyDocument* doc;
|
||||
|
||||
while ((doc = [e nextObject]))
|
||||
{
|
||||
[doc->mScreen MakeCurrentContext];
|
||||
GL_PreparePalette();
|
||||
[doc->mScreen setNeedsDisplay:YES];
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (NSString*)windowNibName
|
||||
{/*
|
||||
"Override returning the nib file name of the document.
|
||||
If you need to use a subclass of NSWindowController or if your document
|
||||
supports multiple NSWindowControllers, you should remove this method
|
||||
and override -makeWindowControllers instead."
|
||||
*/
|
||||
return @"MyDocument";
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (NSDictionary*)fileAttributesToWriteToFile:(NSString*)fpath
|
||||
ofType:(NSString*)docType
|
||||
saveOperation:(NSSaveOperationType)saveOp
|
||||
{/*
|
||||
Tells the OS our preferred HFS type and creator codes for saved
|
||||
documents.
|
||||
*/
|
||||
NSMutableDictionary* dict;
|
||||
|
||||
dict = [NSMutableDictionary dictionaryWithDictionary:
|
||||
[super fileAttributesToWriteToFile:fpath
|
||||
ofType:docType saveOperation:saveOp]];
|
||||
|
||||
[dict setObject:[NSNumber numberWithUnsignedLong:kDocCreatorCode]
|
||||
forKey:NSFileHFSCreatorCode];
|
||||
[dict setObject:[NSNumber numberWithUnsignedLong:kDocTypeCode]
|
||||
forKey:NSFileHFSTypeCode];
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
#if 0
|
||||
- (BOOL)writeToFile:(NSString *)fileName ofType:(NSString *)docType
|
||||
{
|
||||
}
|
||||
|
||||
- (BOOL)readFromFile:(NSString *)fileName ofType:(NSString *)docType
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (NSData*)dataRepresentationOfType:(NSString*)docType
|
||||
{/*
|
||||
"Insert code here to write your document from the given data. You can
|
||||
also choose to override -fileWrapperRepresentationOfType: or
|
||||
-writeToFile:ofType: instead."
|
||||
|
||||
"For applications targeted for Tiger or later systems, you should use
|
||||
the new Tiger API -dataOfType:error:. In this case you can also choose
|
||||
to override -writeToURL:ofType:error:, -fileWrapperOfType:error:, or
|
||||
-writeToURL:ofType:forSaveOperation:originalContentsURL:error: instead."
|
||||
*/
|
||||
// return [NSArchiver archivedDataWithRootObject:self];
|
||||
return [NSKeyedArchiver archivedDataWithRootObject:self];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (BOOL)loadDataRepresentation:(NSData*)data ofType:(NSString*)docType
|
||||
{/*
|
||||
"Insert code here to read your document from the given data. You can
|
||||
also choose to override -loadFileWrapperRepresentation:ofType: or
|
||||
-readFromFile:ofType: instead."
|
||||
|
||||
"For applications targeted for Tiger or later systems, you should use
|
||||
the new Tiger API readFromData:ofType:error:. In this case you can also
|
||||
choose to override -readFromURL:ofType:error: or
|
||||
-readFromFileWrapper:ofType:error: instead."
|
||||
*/
|
||||
MyDocument* doc = [NSKeyedUnarchiver unarchiveObjectWithData:data];
|
||||
|
||||
if (doc == nil)
|
||||
return NO;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder*)enc
|
||||
{
|
||||
[enc encodeObject:mA2 forKey:@"AppleII"];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (id)initWithCoder:(NSCoder*)dec
|
||||
{
|
||||
[super init]; // or [super initWithCoder:dec] if subclassing
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
11
Source/Cocoa/PrefsPanel.h
Normal file
11
Source/Cocoa/PrefsPanel.h
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
@interface PrefsPanel : NSPanel
|
||||
{
|
||||
IBOutlet NSColorWell* mMonochromeHue;
|
||||
IBOutlet NSMatrix* mJoystickControl;
|
||||
}
|
||||
|
||||
- (IBAction) HitJoystickControl:(id)sender;
|
||||
- (IBAction) HitMonochromeHue:(id)sender;
|
||||
|
||||
@end
|
105
Source/Cocoa/PrefsPanel.m
Normal file
105
Source/Cocoa/PrefsPanel.m
Normal file
@ -0,0 +1,105 @@
|
||||
/* class PrefsPanel
|
||||
|
||||
Catakig's Preferences panel. Only one instance exists at runtime.
|
||||
*/
|
||||
#import "Catakig-Cocoa.h"
|
||||
#import "PrefsPanel.h"
|
||||
#import "ScreenView.h"
|
||||
|
||||
@implementation PrefsPanel
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static void SyncDefaults(BOOL registering)
|
||||
{
|
||||
#define PREF(NAME, TYPE, IVALUE) \
|
||||
if (registering) \
|
||||
G.prefs.NAME = [sud Register##TYPE:IVALUE forKey:@ #NAME]; \
|
||||
else \
|
||||
[sud set##TYPE:G.prefs.NAME forKey:@ #NAME];
|
||||
|
||||
NSUserDefaults* sud = [NSUserDefaults standardUserDefaults];
|
||||
|
||||
PREF(firstLaunch, Bool, YES)
|
||||
PREF(beepsOn, Bool, YES)
|
||||
PREF(keepBackupFiles, Bool, NO)
|
||||
PREF(monochromeHue, Integer, 0x33FF33)
|
||||
PREF(joystickControl, Integer, kJoyMouse)
|
||||
|
||||
if (not registering)
|
||||
[sud synchronize];
|
||||
|
||||
#undef PREF
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
SyncDefaults(YES);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)close
|
||||
{
|
||||
G.prefs.firstLaunch = NO;
|
||||
SyncDefaults(NO);
|
||||
|
||||
// NSLog(@"PrefPanel -close called"); //!!
|
||||
// [NSUserDefaults resetStandardUserDefaults];
|
||||
[super close];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
[mJoystickControl selectCellWithTag:G.prefs.joystickControl];
|
||||
|
||||
[mMonochromeHue setColor:[NSColor
|
||||
colorWithDeviceRed: ((G.prefs.monochromeHue>>16) & 255)/255.
|
||||
green: ((G.prefs.monochromeHue>> 8) & 255)/255.
|
||||
blue: ((G.prefs.monochromeHue ) & 255)/255.
|
||||
alpha: 1. ]];
|
||||
[self HitMonochromeHue:mMonochromeHue];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)makeKeyAndOrderFront:(id)sender
|
||||
{
|
||||
if (not [self isVisible])
|
||||
[self center];
|
||||
|
||||
[super makeKeyAndOrderFront:sender];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (IBAction)HitJoystickControl:(id)sender
|
||||
{/*
|
||||
Responds to user changing the joystick (and paddle) control mechanism.
|
||||
*/
|
||||
G.prefs.joystickControl = [sender selectedTag];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (IBAction)HitMonochromeHue:(id)sender
|
||||
{/*
|
||||
Responds to user changing the monochrome video hue preference.
|
||||
|
||||
First ensure color is in RGB space??
|
||||
*/
|
||||
float r, g, b;
|
||||
|
||||
[[sender color] getRed:&r green:&g blue:&b alpha:nil];
|
||||
G.prefs.monochromeHue =
|
||||
(uint32_t)(255. * r) << 16 |
|
||||
(uint32_t)(255. * g) << 8 |
|
||||
(uint32_t)(255. * b);
|
||||
[ScreenView AllNeedDisplay];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
27
Source/Cocoa/ScreenView.h
Normal file
27
Source/Cocoa/ScreenView.h
Normal file
@ -0,0 +1,27 @@
|
||||
|
||||
@class A2Computer, IndicatorLight, MyDocument;
|
||||
|
||||
@interface ScreenView : NSOpenGLView
|
||||
{
|
||||
IBOutlet MyDocument* mDocument;
|
||||
IBOutlet IndicatorLight* mDDLight0;
|
||||
IBOutlet IndicatorLight* mDDLight1;
|
||||
IBOutlet IndicatorLight* mPrinterLight;
|
||||
|
||||
uint8_t mVideoStyle; // bits 6-7 used: monochrome, flash
|
||||
unsigned mPrevLights;
|
||||
|
||||
@public
|
||||
IBOutlet A2Computer* mA2;
|
||||
|
||||
void (*mRenderScreen )(id, SEL, void*, int32_t);
|
||||
void (*mRunForOneStep)(id, SEL, uint8_t*);
|
||||
}
|
||||
|
||||
+ (void) FullScreenOff;
|
||||
+ (void) AllNeedDisplay;
|
||||
- (void) Flash;
|
||||
- (IBAction) ToggleColorVideo:(id)sender;
|
||||
- (IBAction) ToggleFullScreen:(id)sender;
|
||||
|
||||
@end
|
483
Source/Cocoa/ScreenView.m
Normal file
483
Source/Cocoa/ScreenView.m
Normal file
@ -0,0 +1,483 @@
|
||||
/* class ScreenView
|
||||
|
||||
The big view taking up most of an Apple II window, which displays the
|
||||
computer's live video. Subclass of NSOpenGLView.
|
||||
*/
|
||||
#import "Catakig-Cocoa.h"
|
||||
#import "MyDocument.h"
|
||||
#import "ScreenView.h"
|
||||
#import "IndicatorLight.h"
|
||||
|
||||
@implementation ScreenView
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
enum
|
||||
{
|
||||
kCaptureAllScreens = NO,
|
||||
// kUseShieldWindow = NO,
|
||||
kUseOwnGState = YES,
|
||||
kUseSetSystemUIMode = NO,
|
||||
kLockPixels = YES,
|
||||
|
||||
kTexWidth = 1024, // width and ...
|
||||
kTexHeight = 256, // height of shared GL texture
|
||||
kPixelFormat = GL_COLOR_INDEX,
|
||||
kPixelType = GL_UNSIGNED_BYTE,
|
||||
// kBestScreenDepth = 16,
|
||||
|
||||
kfFlash = 1 << 6,
|
||||
kfMonochrome = 1 << 7,
|
||||
};
|
||||
|
||||
static struct
|
||||
{
|
||||
ScreenView* fullScreenScreen;
|
||||
NSOpenGLContext* fullScreenContext;
|
||||
|
||||
NSOpenGLPixelFormat* sharedPixelFormat;
|
||||
NSOpenGLContext* sharedContext;
|
||||
GLuint displayListID;
|
||||
GLuint textureID;
|
||||
uint8_t* pixels; // [kTexHeight][kTexWidth]
|
||||
} g;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static GLuint MakeDisplayList(void)
|
||||
{
|
||||
GLuint listID = glGenLists(1);
|
||||
|
||||
if (listID < 1) // then failed to allocate
|
||||
return 0;
|
||||
|
||||
const GLfloat cw = kA2ScreenWidth / (double)kTexWidth,
|
||||
ch = (kA2ScreenHeight/2) / (double)kTexHeight;
|
||||
|
||||
glNewList(listID, GL_COMPILE);
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
glTexCoord2f(0, 0); glVertex2i(0, 0);
|
||||
glTexCoord2f(0, ch); glVertex2i(0, kA2ScreenHeight);
|
||||
glTexCoord2f(cw, ch); glVertex2i(kA2ScreenWidth, kA2ScreenHeight);
|
||||
glTexCoord2f(cw, 0); glVertex2i(kA2ScreenWidth, 0);
|
||||
|
||||
glEnd();
|
||||
glEndList();
|
||||
return listID;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static GLuint MakeTextureObject(void)
|
||||
{
|
||||
GLuint textureID = 0;
|
||||
|
||||
glGenTextures(1, &textureID);
|
||||
glBindTexture(GL_TEXTURE_2D, textureID);
|
||||
|
||||
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_PRIORITY, 0.); // 0 or 1??
|
||||
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_STORAGE_HINT_APPLE,
|
||||
GL_STORAGE_SHARED_APPLE); // extension!!
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, kTexWidth, kTexHeight, 0,
|
||||
kPixelFormat, kPixelType, g.pixels);
|
||||
|
||||
return textureID;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static void BlessThisContext(NSOpenGLContext* context) //!!
|
||||
{/*
|
||||
Sets up an OpenGLContext the way we like it. Also makes 'context'
|
||||
the current context and leaves it that way on exit.
|
||||
*/
|
||||
[context makeCurrentContext];
|
||||
[context SetSwapInterval:1L];
|
||||
|
||||
glDisable(GL_DITHER);
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_FOG);
|
||||
glDisable(GL_LIGHTING);
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
// glEnableClientState(GL_VERTEX_ARRAY);
|
||||
// glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
glShadeModel(GL_FLAT);
|
||||
glDepthMask(NO); // helpful??
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||
|
||||
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); // need??
|
||||
glHint(GL_CLIP_VOLUME_CLIPPING_HINT_EXT, GL_FASTEST); // extension!!
|
||||
|
||||
glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, YES); // extension!!
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 8); // helpful??
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, kTexWidth);
|
||||
glPixelTransferi(GL_MAP_COLOR, GL_TRUE);
|
||||
|
||||
if (g.displayListID == 0)
|
||||
g.displayListID = MakeDisplayList();
|
||||
if (g.textureID == 0)
|
||||
g.textureID = MakeTextureObject();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, g.textureID);
|
||||
GL_PreparePalette();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static NSOpenGLContext* MakeFullScreenContext(CGDirectDisplayID dpy)
|
||||
{/*
|
||||
Assumes full-screen mode is already active.
|
||||
*/
|
||||
NSOpenGLPixelFormatAttribute pixFmtAttrs[] =
|
||||
{
|
||||
NSOpenGLPFAFullScreen,
|
||||
NSOpenGLPFAScreenMask, CGDisplayIDToOpenGLDisplayMask(dpy),
|
||||
NSOpenGLPFASingleRenderer,
|
||||
NSOpenGLPFAAccelerated,
|
||||
NSOpenGLPFANoRecovery,
|
||||
NSOpenGLPFADoubleBuffer,
|
||||
|
||||
// NSOpenGLPFAColorSize, 16,
|
||||
NSOpenGLPFAAlphaSize, 0,
|
||||
NSOpenGLPFADepthSize, 0,
|
||||
NSOpenGLPFAStencilSize, 0,
|
||||
NSOpenGLPFAAccumSize, 0,
|
||||
0 };
|
||||
NSOpenGLPixelFormat* pixFmt;
|
||||
NSOpenGLContext* context = nil;
|
||||
|
||||
[(pixFmt = [[NSOpenGLPixelFormat alloc]
|
||||
initWithAttributes:pixFmtAttrs]) autorelease];
|
||||
context = [[NSOpenGLContext alloc]
|
||||
initWithFormat:pixFmt shareContext:nil];
|
||||
|
||||
BlessThisContext(context);
|
||||
MakeDisplayList();
|
||||
glBindTexture(GL_TEXTURE_2D, MakeTextureObject());
|
||||
GL_PrepareViewport(CGDisplayPixelsWide(dpy), CGDisplayPixelsHigh(dpy));
|
||||
GL_ClearBothBuffers();
|
||||
|
||||
[context setFullScreen];
|
||||
return context;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
NSOpenGLPixelFormatAttribute pixFmtAttrs[] =
|
||||
{
|
||||
NSOpenGLPFAWindow,
|
||||
NSOpenGLPFAAccelerated,
|
||||
NSOpenGLPFANoRecovery,
|
||||
NSOpenGLPFADoubleBuffer,
|
||||
// NSOpenGLPFABackingStore,
|
||||
// NSOpenGLPFAPixelBuffer,
|
||||
|
||||
NSOpenGLPFAColorSize, 16,
|
||||
NSOpenGLPFAAlphaSize, 0,
|
||||
NSOpenGLPFADepthSize, 0,
|
||||
NSOpenGLPFAStencilSize, 0,
|
||||
NSOpenGLPFAAccumSize, 0,
|
||||
0 };
|
||||
|
||||
g.pixels = NSAllocateMemoryPages(kTexWidth * kTexHeight);
|
||||
if (g.pixels != nil)
|
||||
{
|
||||
madvise(g.pixels, kTexWidth * kTexHeight, MADV_SEQUENTIAL);
|
||||
if (kLockPixels)
|
||||
mlock(g.pixels, kTexWidth * kTexHeight);
|
||||
}
|
||||
|
||||
g.sharedPixelFormat = [[NSOpenGLPixelFormat alloc]
|
||||
initWithAttributes:pixFmtAttrs];
|
||||
g.sharedContext = [[NSOpenGLContext alloc]
|
||||
initWithFormat:g.sharedPixelFormat shareContext:nil];
|
||||
BlessThisContext(g.sharedContext);
|
||||
|
||||
// NSLog(@"GL version = '%s'", glGetString(GL_VERSION));//!!
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (id)initWithFrame:(NSRect)frame
|
||||
{
|
||||
// NSLog(@"SV -initWithFrame called"); //!!
|
||||
|
||||
self = [super initWithFrame:frame pixelFormat:g.sharedPixelFormat];
|
||||
if (self == nil)
|
||||
return nil;
|
||||
|
||||
if (kUseOwnGState)
|
||||
[self allocateGState]; // helpful??
|
||||
|
||||
mRenderScreen = (void*) [A2Computer instanceMethodForSelector:
|
||||
@selector(RenderScreen::) ];
|
||||
mRunForOneStep = (void*) [A2Computer instanceMethodForSelector:
|
||||
@selector(RunForOneStep:) ];
|
||||
|
||||
// Need to release this view's previous GLContext??
|
||||
[self setOpenGLContext:[[NSOpenGLContext alloc]
|
||||
initWithFormat: g.sharedPixelFormat
|
||||
shareContext: g.sharedContext ]];
|
||||
BlessThisContext([self openGLContext]);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
if (kUseOwnGState)
|
||||
[self releaseGState]; // need this??
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)prepareOpenGL
|
||||
{/*
|
||||
"Used by subclasses to initialize OpenGL state."
|
||||
|
||||
"This method is called only once after the OpenGL context is made the
|
||||
current context. Subclasses that implement this method can use it to
|
||||
configure the Open GL state in preparation for drawing."
|
||||
*/
|
||||
// NSLog(@"SV -prepareOpenGL called"); //!!
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)reshape
|
||||
{/*
|
||||
"Called by Cocoa when the view's visible rectangle or bounds change."
|
||||
|
||||
"Called if the visible rectangle or bounds of the receiver change
|
||||
(for scrolling or resize). The default implementation does nothing.
|
||||
Override this method if you need to adjust the viewport and display
|
||||
frustum."
|
||||
*/
|
||||
NSSize vsize = NSIntegralRect([self bounds]).size;
|
||||
|
||||
GL_PrepareViewport(vsize.width, vsize.height);
|
||||
GL_ClearBothBuffers();
|
||||
|
||||
// NSLog(@"SV -reshape called"); //!!
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)drawRect:(NSRect)r
|
||||
{
|
||||
NSOpenGLContext* context;
|
||||
|
||||
if (g.fullScreenScreen == self)
|
||||
[(context = g.fullScreenContext) makeCurrentContext];
|
||||
else
|
||||
context = [self openGLContext];
|
||||
|
||||
mRenderScreen(mA2, nil, g.pixels, kTexWidth);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kA2ScreenWidth, 192,
|
||||
kPixelFormat, kPixelType, g.pixels);
|
||||
glCallList(g.displayListID);
|
||||
[context flushBuffer];
|
||||
|
||||
// if (g.fullScreenScreen == self)
|
||||
// [self MakeCurrentContext]; // need to restore previous context??
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (IBAction)ToggleColorVideo:(id)sender
|
||||
{/*
|
||||
Toggles this Apple II's video style between monochrome and color.
|
||||
*/
|
||||
mVideoStyle ^= kfMonochrome;
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (IBAction)ToggleFullScreen:(id)sender
|
||||
{/*
|
||||
Toggles this Apple II between full-screen mode and windowed mode.
|
||||
*/
|
||||
static CGDirectDisplayID dpy;
|
||||
static CFDictionaryRef prevMode;
|
||||
|
||||
[mDocument Pause];
|
||||
|
||||
if (g.fullScreenScreen == nil) // then take over user's screen
|
||||
{
|
||||
dpy = CGMainDisplayID(); // always main display??
|
||||
prevMode = CGDisplayCurrentMode(dpy);
|
||||
|
||||
[NSCursor hide];
|
||||
[NSMenu setMenuBarVisible:NO];
|
||||
|
||||
if (kCaptureAllScreens)
|
||||
CGCaptureAllDisplays();
|
||||
else
|
||||
CGDisplayCapture(dpy);
|
||||
|
||||
CGDisplaySwitchToMode(dpy,
|
||||
CGDisplayBestModeForParameters(dpy, 16, 640, 480, nil) );
|
||||
if (kUseSetSystemUIMode)
|
||||
SetSystemUIMode(kUIModeAllHidden, 0); // disables cmd-tab, etc.
|
||||
|
||||
g.fullScreenContext = MakeFullScreenContext(dpy);
|
||||
g.fullScreenScreen = self;
|
||||
}
|
||||
else // relinquish screen and restore desktop
|
||||
{
|
||||
[self MakeCurrentContext];
|
||||
GL_ClearBothBuffers(); // erase old content
|
||||
|
||||
g.fullScreenScreen = nil;
|
||||
g.fullScreenContext = [g.fullScreenContext Release];
|
||||
|
||||
if (kUseSetSystemUIMode)
|
||||
SetSystemUIMode(kUIModeNormal, 0);
|
||||
CGDisplaySwitchToMode(dpy, prevMode);
|
||||
CGReleaseAllDisplays();
|
||||
|
||||
[NSMenu setMenuBarVisible:YES];
|
||||
[NSCursor unhide];
|
||||
}
|
||||
|
||||
SetMouseRange();
|
||||
[self setNeedsDisplay:YES];
|
||||
[mDocument Unpause];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)Flash
|
||||
{/*
|
||||
Should be called a few times per second (say 3 or 4) to implement
|
||||
flashing, and to update the indicator lights.
|
||||
*/
|
||||
unsigned lchg = mPrevLights ^ [mA2 Lights];
|
||||
|
||||
if (lchg != 0)
|
||||
{
|
||||
mPrevLights ^= lchg;
|
||||
|
||||
if (lchg & kfA2LightPrinter) [mPrinterLight ToggleState];
|
||||
if (lchg & kfA2LightDDrive0) [mDDLight0 ToggleState];
|
||||
if (lchg & kfA2LightDDrive1) [mDDLight1 ToggleState];
|
||||
}
|
||||
|
||||
if (g.fullScreenContext)
|
||||
[g.fullScreenContext makeCurrentContext];
|
||||
else
|
||||
[self MakeCurrentContext];
|
||||
|
||||
glPixelTransferi(GL_INDEX_OFFSET, (mVideoStyle ^= kfFlash));
|
||||
[self setNeedsDisplay:YES];
|
||||
|
||||
#if 0
|
||||
if ([mDocument IsRunning])
|
||||
[[self window] setDocumentEdited:YES];
|
||||
#endif
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)keyDown:(NSEvent*)event
|
||||
{
|
||||
// if (not [mDocument IsRunning]) // then ignore keypresses
|
||||
// return;
|
||||
|
||||
// NSString* chstr = [event charactersIgnoringModifiers];
|
||||
NSString* chstr = [event characters];
|
||||
|
||||
if (chstr == nil or [chstr length] < 1)
|
||||
return;
|
||||
|
||||
unsigned mods = [event modifierFlags],
|
||||
ch = [chstr characterAtIndex:0];
|
||||
BOOL keyInNumPad = ((mods & NSNumericPadKeyMask) != 0);
|
||||
|
||||
// NSLog(@"char %04X, mods %08X", ch, mods); //!!
|
||||
|
||||
switch (ch)
|
||||
{
|
||||
case NSLeftArrowFunctionKey: ch = 8; break;
|
||||
case NSRightArrowFunctionKey: ch = 21; break;
|
||||
case NSDownArrowFunctionKey: ch = 10; break;
|
||||
case NSUpArrowFunctionKey: ch = 11; break;
|
||||
case NSClearLineFunctionKey: ch = 27; break;
|
||||
|
||||
case 3: if (keyInNumPad) // then it's 'enter', not ctrl-C
|
||||
ch = 13;
|
||||
break;
|
||||
|
||||
case '7': case '8': case '9':
|
||||
case '4': case '5': case '6':
|
||||
case '1': case '2': case '3':
|
||||
if (keyInNumPad and G.prefs.joystickControl == kJoyKeypad)
|
||||
{
|
||||
[A2Computer InputPaddlesByKeypad:ch];
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
[mA2 InputChar:ch];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (BOOL)performKeyEquivalent:(NSEvent*)event
|
||||
{/*
|
||||
"Returns YES if theEvent is a key equivalent that the receiver handled,
|
||||
NO if it is not a key equivalent that it should handle."
|
||||
|
||||
"If the receiver’s key equivalent is the same as the characters of the
|
||||
key-down event theEvent, as returned by charactersIgnoringModifiers,
|
||||
the receiver should take the appropriate action and return YES.
|
||||
Otherwise, it should return the result of invoking super’s
|
||||
implementation. The default implementation of this method simply passes
|
||||
the message down the view hierarchy (from superviews to subviews) and
|
||||
returns NO if none of the receiver’s subviews responds YES."
|
||||
*/
|
||||
if (g.fullScreenScreen != nil and
|
||||
([event modifierFlags] & NSCommandKeyMask) )
|
||||
return YES;
|
||||
|
||||
return [super performKeyEquivalent:event];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
//- (BOOL)isFlipped { return YES; }
|
||||
|
||||
+ (void)FullScreenOff
|
||||
{ [g.fullScreenScreen ToggleFullScreen:self]; }
|
||||
|
||||
+ (void)AllNeedDisplay
|
||||
{ [MyDocument AllNeedDisplay]; }
|
||||
|
||||
- (BOOL)acceptsFirstResponder
|
||||
{ return YES; } // yes, we want those keypresses
|
||||
|
||||
- (BOOL)isOpaque
|
||||
{ return YES; } // eliminates unsightly flickering
|
||||
|
||||
- (BOOL)wantsDefaultClipping
|
||||
{ return NO; } // bypasses clip-rect preparation
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
241
Source/Cocoa/main.m
Normal file
241
Source/Cocoa/main.m
Normal file
@ -0,0 +1,241 @@
|
||||
/* main.m
|
||||
|
||||
The application's global functions, including 'main'.
|
||||
*/
|
||||
#import "Catakig-Cocoa.h"
|
||||
|
||||
struct CatakigGlobals G; // application-wide global variables
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
return NSApplicationMain(argc, argv);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void BeepFor(BOOL success)
|
||||
{/*
|
||||
Optionally plays a sound, depending on user's preferences, to
|
||||
indicate success or failure.
|
||||
*/
|
||||
if (G.prefs.beepsOn)
|
||||
[[NSSound soundNamed:(success? @"Glass" : @"Sosumi")] play];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void ErrorAlert(NSWindow* window, NSString* title, NSString* msg)
|
||||
{
|
||||
NSString* defaultButton = @"Okay"; //??
|
||||
|
||||
BeepFor(NO);
|
||||
|
||||
#if 0
|
||||
if (errno != 0)
|
||||
msg = [msg stringByAppendingString:
|
||||
[NSString stringWithFormat:@"\n\n(%d) %s",
|
||||
errno, strerror(errno)]];
|
||||
#endif
|
||||
|
||||
if (window != nil)
|
||||
NSBeginCriticalAlertSheet(title, defaultButton, nil, nil,
|
||||
window, nil, nil, nil, nil, msg);
|
||||
else
|
||||
NSRunCriticalAlertPanel(title, msg, defaultButton, nil, nil);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void FatalErrorAlert(NSString* title, NSString* msg)
|
||||
{
|
||||
BeepFor(NO);
|
||||
NSRunCriticalAlertPanel(title, msg, nil, nil, nil);
|
||||
[NSApp terminate:nil];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void SetMouseRange(void)
|
||||
{
|
||||
CGDirectDisplayID dpy = CGMainDisplayID();
|
||||
union {
|
||||
CGRect cg;
|
||||
NSRect ns;
|
||||
} bounds;
|
||||
|
||||
bounds.cg = CGDisplayBounds(dpy);
|
||||
|
||||
if (NO)
|
||||
NSLog(@"main display bounds: (%f %f) (%f %f)",
|
||||
bounds.ns.origin.x, bounds.ns.origin.y,
|
||||
bounds.ns.size.width, bounds.ns.size.height);
|
||||
|
||||
if (CGDisplayIsCaptured(dpy))
|
||||
bounds.ns.origin.y =
|
||||
[[NSScreen MenuBarScreen] frame].size.height -
|
||||
CGDisplayPixelsHigh(dpy);
|
||||
|
||||
[A2Computer SetMouseRangeTo:bounds.ns];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
OSStatus AU_Open(AudioUnit* audioUnit)
|
||||
{/*
|
||||
Allocates and initializes a new output AudioUnit.
|
||||
*/
|
||||
OSStatus sts;
|
||||
UInt32 n;
|
||||
Component comp;
|
||||
ComponentDescription compDesc =
|
||||
{
|
||||
.componentType = kAudioUnitType_Output,
|
||||
.componentSubType = kAudioUnitSubType_DefaultOutput,
|
||||
.componentManufacturer = kAudioUnitManufacturer_Apple,
|
||||
.componentFlags = 0,
|
||||
.componentFlagsMask = 0,
|
||||
};
|
||||
|
||||
*audioUnit = 0;
|
||||
if (NULL == (comp = FindNextComponent(NULL, &compDesc)))
|
||||
return fnfErr;
|
||||
if (noErr != (sts = OpenAComponent(comp, audioUnit)))
|
||||
return sts;
|
||||
#if 0
|
||||
sts = AudioUnitSetProperty(*audioUnit,
|
||||
kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
|
||||
0, (n=364, &n), sizeof(n));
|
||||
#endif
|
||||
|
||||
return AudioUnitInitialize(*audioUnit);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
OSStatus AU_SetBufferFrameSize(AudioUnit audioUnit, UInt32 frameSize)
|
||||
{/*
|
||||
Sets the number of frames-per-buffer used by the given AudioUnit.
|
||||
*/
|
||||
OSStatus sts;
|
||||
UInt32 n;
|
||||
AudioDeviceID device = 0;
|
||||
|
||||
sts = AudioUnitGetProperty(audioUnit,
|
||||
kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global,
|
||||
0, &device, (n=sizeof(device), &n));
|
||||
if (sts != noErr)
|
||||
return sts;
|
||||
|
||||
sts = AudioDeviceSetProperty(device, NULL, 0, NO,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
sizeof(frameSize), &frameSize);
|
||||
|
||||
#if 0
|
||||
AudioDeviceGetProperty(device, 0, NO,
|
||||
kAudioDevicePropertyBufferFrameSize,
|
||||
(n=sizeof(frameSize), &n), &frameSize);
|
||||
NSLog(@"Device frame size is now %ld", frameSize);
|
||||
|
||||
Float64 rate;
|
||||
AudioDeviceGetProperty(device, 0, NO,
|
||||
kAudioDevicePropertyNominalSampleRate,
|
||||
(n=sizeof(rate), &n), &rate);
|
||||
NSLog(@"Device nominal sample rate is %.2f", rate);
|
||||
#endif
|
||||
|
||||
return sts;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void AU_Close(AudioUnit* audioUnit)
|
||||
{
|
||||
if (*audioUnit) // is open...
|
||||
{
|
||||
AudioOutputUnitStop(*audioUnit);
|
||||
AudioUnitUninitialize(*audioUnit);
|
||||
CloseComponent(*audioUnit);
|
||||
}
|
||||
*audioUnit = 0;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
BOOL GL_CheckExtension(const char* name)
|
||||
{/*
|
||||
Tests whether a given OpenGL extension is supported.
|
||||
*/
|
||||
return gluCheckExtension((const GLubyte*)name,
|
||||
glGetString(GL_EXTENSIONS));
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void GL_ClearBothBuffers(void)
|
||||
{/*
|
||||
Clears both the front and back buffers of the current GL context.
|
||||
*/
|
||||
NSOpenGLContext* context = [NSOpenGLContext currentContext];
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT); [context flushBuffer];
|
||||
glClear(GL_COLOR_BUFFER_BIT); [context flushBuffer];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void GL_PrepareViewport(int viewWidth, int viewHeight)
|
||||
{/*
|
||||
Sets up a centered, orthogonal projection for the current GL context.
|
||||
*/
|
||||
int hMargin = (viewWidth - kA2ScreenWidth ) / 2,
|
||||
vMargin = (viewHeight - kA2ScreenHeight) / 2;
|
||||
|
||||
glViewport(0, 0, viewWidth, viewHeight);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glOrtho(-hMargin, viewWidth - hMargin,
|
||||
viewHeight - vMargin, -vMargin,
|
||||
0, 1);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void GL_PreparePalette(void)
|
||||
{
|
||||
for (int rgb = 3; --rgb >= 0;)
|
||||
{
|
||||
GLushort pal[4][64];
|
||||
GLushort mono;
|
||||
|
||||
memset(pal, 0, sizeof(pal));
|
||||
mono = 0x0101 * (G.prefs.monochromeHue >> (8*(2-rgb)) & 0xFF);
|
||||
|
||||
for (int i = 16; --i >= 0;)
|
||||
{
|
||||
pal[0][0x00|i] = pal[0][0x10|i] =
|
||||
pal[0][0x20|i] = 0x1111 *
|
||||
(A2G.standardColors[i]>>(4*(2-rgb)) & 15);
|
||||
|
||||
pal[2][0x10|i] = mono;
|
||||
pal[2][0x20|i] = mono * ("0113123413243445"[i] & 7L) / 5;
|
||||
}
|
||||
memcpy(pal[1], pal[0], sizeof(pal[0]));
|
||||
memcpy(pal[3], pal[2], sizeof(pal[2]));
|
||||
|
||||
pal[0][0x33] = pal[1][0x32] =
|
||||
pal[0][0x31] = pal[1][0x31] = 0xFFFF;
|
||||
|
||||
pal[2][0x33] = pal[3][0x32] =
|
||||
pal[2][0x31] = pal[3][0x31] = mono;
|
||||
|
||||
glPixelMapusv(GL_PIXEL_MAP_I_TO_R + rgb, 256, pal[0]);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
170
Source/LibAppleII/A2Computer/A2Computer.m
Normal file
170
Source/LibAppleII/A2Computer/A2Computer.m
Normal file
@ -0,0 +1,170 @@
|
||||
/* class A2Computer
|
||||
|
||||
An object representing an Apple II computer. Methods in this source
|
||||
file are for object allocation, deallocation, initialization, and
|
||||
serialization.
|
||||
*/
|
||||
#import "LibAppleII-Priv.h"
|
||||
#import "A2DiskDrive.h"
|
||||
|
||||
@implementation A2Computer
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self != [A2Computer class])
|
||||
return; // ensures this routine executes no more than once
|
||||
|
||||
[A2Computer _InitAudio];
|
||||
[A2Computer _InitVideo];
|
||||
[A2Computer _InitCPU];
|
||||
[A2Computer _InitROM];
|
||||
[A2Computer _InitPrinting];
|
||||
|
||||
[A2Computer SetMouseRangeTo:NSMakeRect(0, 0, 640, 480)];
|
||||
[A2Computer setVersion:1]; //??
|
||||
mlock(&A2T, sizeof(A2T));
|
||||
|
||||
[NSTimer scheduledTimerWithTimeInterval:0.75
|
||||
target: [A2Computer class]
|
||||
selector: @selector(_UpdateClock:)
|
||||
userInfo: nil
|
||||
repeats: YES ];
|
||||
|
||||
if (NSPageSize() > 0x2000)
|
||||
NSLog(@"Warning: VM page size = %ld (> 0x2000)", NSPageSize());
|
||||
#if 0
|
||||
NSLog(@"A2Computer size = %lu", sizeof(struct{@defs(A2Computer)}));
|
||||
NSLog(@"VM page size = 0x%X", NSPageSize());
|
||||
NSLog(@"A2T size = %lu", sizeof(A2T));
|
||||
#endif
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (id)init
|
||||
{/*
|
||||
"Add your subclass-specific initialization here. If an error occurs,
|
||||
send a [self release] message and return nil."
|
||||
|
||||
Not robust enough against failures!!
|
||||
*/
|
||||
if (nil == (self = [super init]))
|
||||
return nil;
|
||||
|
||||
mModel = A2G.defaultModel;
|
||||
mFlags = kfTEXT;
|
||||
mHalts = kfHaltNoPower | kfHaltReset;
|
||||
mMemorySize = sizeof(A2Memory);
|
||||
mPrinter.session = tmpfile();
|
||||
mSlinky.mask = (1L << A2G.defaultExtraRAM) - 1;
|
||||
mSlinky.base = &mSlinky.nowhere;
|
||||
|
||||
if (mSlinky.mask != 0)
|
||||
mMemorySize += (mSlinky.mask + 1);
|
||||
mMemory = NSAllocateMemoryPages(mMemorySize);
|
||||
|
||||
if (not mMemory or not mPrinter.session)
|
||||
return [self Release];
|
||||
|
||||
if (mSlinky.mask != 0)
|
||||
mSlinky.base = (uint8_t*)mMemory + sizeof(A2Memory);
|
||||
|
||||
// Create the disk drives, and give every one a track buffer to
|
||||
// work with.
|
||||
|
||||
for (int dd = 4; --dd >= 0;)
|
||||
mIWM[dd>>1].drive[dd&1] = [[A2DiskDrive alloc]
|
||||
InitUsingBuffer: mMemory->diskBuffers[dd] ];
|
||||
|
||||
// Initialize video memory with random bytes (a theatrical effect).
|
||||
|
||||
for (int i = 0x6000; --i >= 0;)
|
||||
((uint16_t*)(mMemory->RAM))[i] = A2Random16();
|
||||
|
||||
madvise(mMemory, mMemorySize, MADV_SEQUENTIAL);
|
||||
[self _PrepareModel];
|
||||
return self;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)_TestThePrinter // called only for debugging!!
|
||||
{
|
||||
fputs(
|
||||
"---------1---------2---------3---------4"
|
||||
"---------5---------6---------7---------8\r\n\r\n"
|
||||
" !\"#$%&'()*+,-./0123456789:;<=>?\r\n"
|
||||
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_\r\n"
|
||||
"`abcdefghijklmnopqrstuvwxyz{|}~\r\n\r\n"
|
||||
"\x1B\x34Hello world!\x1B@\r\n", mPrinter.session);
|
||||
fprintf(mPrinter.session,
|
||||
"\x1BK\x07%c\1\2\3\4\5\6\7 |\r\n", 0);
|
||||
for (int i = 0; i < 100; ++i)
|
||||
fprintf(mPrinter.session, "%d\t%d\r\n", i, i*i);
|
||||
|
||||
[self SavePrintSessionAs:kA2PFPlain
|
||||
toFile:@"/Users/klipsch/Desktop/printout.txt"];
|
||||
[self SavePrintSessionAs:kA2PFEpsonToPS
|
||||
toFile:@"/Users/klipsch/Desktop/printout.ps"];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
// [self _TestThePrinter];
|
||||
|
||||
for (int dd = 4; --dd >= 0;)
|
||||
[mIWM[dd>>1].drive[dd&1] release];
|
||||
|
||||
if (mMemory != nil)
|
||||
NSDeallocateMemoryPages(mMemory, mMemorySize);
|
||||
|
||||
fclose(mPrinter.session);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder*)enc // experimental!!
|
||||
{
|
||||
[enc encodeArrayOfObjCType:@encode(uint8_t) count:7 at:&mA];
|
||||
[enc encodeArrayOfObjCType:@encode(uint16_t) count:2 at:&mPC];
|
||||
[enc encodeArrayOfObjCType:@encode(uint32_t) count:4 at:&mFlags];
|
||||
|
||||
[enc encodeBytes:mMemory->RAM length:2*k64KB];
|
||||
|
||||
for (int i = 0; i <= 1; i++)
|
||||
{
|
||||
A2IWM* iwm = mIWM + i;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (id)initWithCoder:(NSCoder*)dec // experimental!!
|
||||
{
|
||||
if (nil == (self = [super init]))
|
||||
return nil;
|
||||
|
||||
void* ptr;
|
||||
unsigned len;
|
||||
|
||||
[dec decodeArrayOfObjCType:@encode(uint8_t) count:7 at:&mA];
|
||||
[dec decodeArrayOfObjCType:@encode(uint16_t) count:2 at:&mPC];
|
||||
[dec decodeArrayOfObjCType:@encode(uint32_t) count:4 at:&mFlags];
|
||||
|
||||
ptr = [dec decodeBytesWithReturnedLength:&len];
|
||||
memcpy(mMemory->RAM, ptr, len);
|
||||
|
||||
for (int i = 0; i <= 1; i++)
|
||||
{
|
||||
A2IWM* iwm = mIWM + i;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
94
Source/LibAppleII/A2Computer/Audio.m
Normal file
94
Source/LibAppleII/A2Computer/Audio.m
Normal file
@ -0,0 +1,94 @@
|
||||
/* class A2Computer (category Audio)
|
||||
*/
|
||||
#import "LibAppleII-Priv.h"
|
||||
|
||||
@implementation A2Computer (Audio)
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static struct
|
||||
{
|
||||
int silence; // = 0 or 128
|
||||
double FIR[kFilterSize]; // partial sums of FIR coefficients
|
||||
|
||||
} g =
|
||||
{
|
||||
.silence = 128,
|
||||
};
|
||||
|
||||
|
||||
static double Sinc(double x)
|
||||
{ return x? sin(x)/x : 1.; }
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
+ (void)_InitAudio
|
||||
{/*
|
||||
Called once at startup, from '+initialize'. Prepares the digital filter
|
||||
coefficients, and sets a default volume level.
|
||||
*/
|
||||
const double pi = 4. * atan(1.),
|
||||
c = pi * 0.4 / kFilterRes;
|
||||
double psum = 0.,
|
||||
norm = 0.;
|
||||
|
||||
for (int j = kFilterSize; --j >= 0;)
|
||||
{
|
||||
int i = 2*j - kFilterSize + 1;
|
||||
double h = Sinc(c * i);
|
||||
// double x = i * (pi / kFilterSize);
|
||||
|
||||
// h *= 42 + 50*cos(x) + 8*cos(2*x); // Blackman
|
||||
// h *= 54 + 46*cos(x); // Hamming
|
||||
// h *= 50 + 50*cos(x); // Hann
|
||||
|
||||
norm += ( g.FIR[j] = h );
|
||||
// NSLog(@"h[%4d] = %f", j, h);
|
||||
}
|
||||
|
||||
for (int j = kFilterSize; --j >= 0;)
|
||||
g.FIR[j] = ( psum += (g.FIR[j] / norm) );
|
||||
|
||||
[A2Computer SetAudioVolume:40];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
+ (void)SetAudioVolume:(unsigned)volume
|
||||
{/*
|
||||
Sets the volume level for audio production. Parameter 'volume' should
|
||||
be between 0 and 100.
|
||||
*/
|
||||
if (volume > 100)
|
||||
volume = 100;
|
||||
A2T.audio.flat = (g.silence - volume - 1L) << 24;
|
||||
|
||||
long range = -256L * (2 * (int)volume + 1);
|
||||
|
||||
for (int i = kFilterSize; --i >= 0;)
|
||||
A2T.audio.delta[i] =
|
||||
((long)(g.FIR[i] * range + 0.5)) << 16;
|
||||
|
||||
#if 0
|
||||
for (int i = LENGTH(gSpkrOut); --i >= 0;)
|
||||
gSpkrOut[i] = A2T.audio.flat; //??
|
||||
#endif
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)_DefaultAudio:(uint8_t [])audioOut :(unsigned)nSamples
|
||||
{
|
||||
if (mHalts & kfHaltNoPower) // then power is off; emit hiss
|
||||
{
|
||||
for (int i = nSamples/4; --i >= 0;)
|
||||
((uint32_t*)audioOut)[i] = 0x01010101 *
|
||||
( (g.silence - 8) + (A2Random16() & 15) );
|
||||
}
|
||||
else // power is on, but execution still halted; emit silence
|
||||
{
|
||||
memset(audioOut, g.silence, nSamples);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
46
Source/LibAppleII/A2Computer/CPU-Journal.h
Normal file
46
Source/LibAppleII/A2Computer/CPU-Journal.h
Normal file
@ -0,0 +1,46 @@
|
||||
/* CPU-Journal.h
|
||||
|
||||
A helper file used to debug the 65c02 interpreter. Not included
|
||||
in released versions of the LibAppleII library.
|
||||
*/
|
||||
|
||||
enum { kPClo = 0x2000, kPChi = 0x3000 };
|
||||
|
||||
#define JOURNALING 1
|
||||
#undef JOURNAL_OP
|
||||
#undef JOURNAL_EA
|
||||
|
||||
#define JOURNAL_OP \
|
||||
if (not jstop and pc >= kPClo and pc <= kPChi) { \
|
||||
jdata[++jnum].pc = pc; jdata[ jnum].op = d; \
|
||||
jdata[ jnum].p = p; jdata[ jnum].a = mA; \
|
||||
jdata[ jnum].x = mX; jdata[ jnum].y = mY; \
|
||||
jdata[ jnum].ea = -1; \
|
||||
}
|
||||
|
||||
#define JOURNAL_EA jdata[jnum].ea = ea;
|
||||
|
||||
static struct
|
||||
{
|
||||
uint16_t pc, p;
|
||||
int ea;
|
||||
uint8_t op, a, x, y;
|
||||
} jdata[256];
|
||||
static uint8_t jnum;
|
||||
static BOOL jstop; // = NO
|
||||
|
||||
|
||||
static void LogJournal(void)
|
||||
{
|
||||
for (int i = 1, j; i <= 256; ++i)
|
||||
{
|
||||
j = (jnum+i) & 0xFF;
|
||||
fprintf(stderr,
|
||||
"A=%02X X=%02X Y=%02X p=%03X | %04X- %02X",
|
||||
jdata[j].a, jdata[j].x, jdata[j].y, jdata[j].p & 0xFFF,
|
||||
jdata[j].pc, jdata[j].op);
|
||||
if (jdata[j].ea >= 0)
|
||||
fprintf(stderr, " $%04X", jdata[j].ea);
|
||||
fputs("\n", stderr);
|
||||
}
|
||||
}
|
86
Source/LibAppleII/A2Computer/CPU-Macros.h
Normal file
86
Source/LibAppleII/A2Computer/CPU-Macros.h
Normal file
@ -0,0 +1,86 @@
|
||||
// Macros... So many macros...
|
||||
|
||||
// For defining multiple 'case' labels easily:
|
||||
|
||||
#define CASE7(B,D) \
|
||||
case B : case B + D: case B + 2*D: case B + 3*D: \
|
||||
case B + 4*D: case B + 5*D: case B + 6*D
|
||||
|
||||
#define CASE8(B,D) CASE7(B,D): case B + 7*D
|
||||
|
||||
#define CASE15(B,D) CASE8(B,D): \
|
||||
case B + 8*D: case B + 9*D: case B + 10*D: case B + 11*D: \
|
||||
case B + 12*D: case B + 13*D: case B + 14*D
|
||||
|
||||
#define CASE16(B,D) CASE15(B,D): case B + 15*D
|
||||
|
||||
|
||||
// Reading from and writing to memory:
|
||||
|
||||
#define REMAP_(F) (( zp = mMemory->RAM[0][(F)>>ksALTZP & 1] ),\
|
||||
( rmap = A2T.rmaps[(F) >> ksRMap & kmRMap] ),\
|
||||
( wmap = A2T.wmaps[(F) >> ksWMap & kmWMap] ))
|
||||
|
||||
#define READ(ADDR) (zp + ADDR)[rmap[ADDR>>9] << 11]
|
||||
#define WRITE(ADDR) (zp + ADDR)[wmap[ADDR>>9] << 11]
|
||||
#define READ_PC (++pc, READ(pc))
|
||||
|
||||
#define PUSH(BYTE) zp[0x100 | mS--] = (BYTE)
|
||||
#define PULL zp[0x100 | ++mS]
|
||||
#define PHP PUSH(A2T.tPHP[p & kmPHP] | mI)
|
||||
#define PLP mI = 4 & (p = PULL); p = A2T.tPLP[p]
|
||||
|
||||
|
||||
// Computing effective addresses, into local variable 'ea':
|
||||
|
||||
#define EAZ ea = READ_PC
|
||||
#define EAZX ea = (READ_PC + mX) & 0xFF
|
||||
#define EAZY ea = (READ_PC + mY) & 0xFF
|
||||
#define EAA EAZ; ea += READ_PC<<8
|
||||
#define EAAX EAA | mX
|
||||
#define EAAY EAA | mY
|
||||
#define EAI EAZ ; ea = ((zp+1)[ea]<<8 | zp[ea])
|
||||
#define EAIX EAZX; ea = ((zp+1)[ea]<<8 | zp[ea])
|
||||
#define EAIY EAI + mY
|
||||
|
||||
|
||||
// Implementations of most 65c02 instructions:
|
||||
|
||||
#define SET_NZ p = (p & kfCDV) | (d << ksLSB)
|
||||
#define INC ++d; SET_NZ
|
||||
#define DEC --d; SET_NZ
|
||||
#define LDA mA = d; SET_NZ
|
||||
#define LDX mX = d; SET_NZ
|
||||
#define LDY mY = d; SET_NZ
|
||||
#define AND mA = d &= mA; SET_NZ
|
||||
#define EOR mA = d ^= mA; SET_NZ
|
||||
#define ORA mA = d |= mA; SET_NZ
|
||||
|
||||
#define CP_(REG) d = REG-d; p = p&kfDV | d<<ksLSB | (~d>>7 & kfC)
|
||||
#define CMP CP_(mA)
|
||||
#define CPX CP_(mX)
|
||||
#define CPY CP_(mY)
|
||||
|
||||
#define AD_SB_C(T,D) ( (mA = d = (long) T[T[mA] + T[D] + (p&kfCD)]), \
|
||||
(p = d >> 8) )
|
||||
#define ADC AD_SB_C(mTblADC, d)
|
||||
#define SBC AD_SB_C(mTblSBC, d ^ 0xFF)
|
||||
|
||||
#define BITZ p = kfLSB | (d&mA? (p & ~kfZF) : (p | kfZF))
|
||||
#define BIT p = p&kfCD | d<<ksLSB | d>>4&kfV; BITZ
|
||||
#define TRB BITZ; d &= ~mA
|
||||
#define TSB BITZ; d |= mA
|
||||
#define Z_SET ( ((p & kmZ8) == 0) | (p & kfZF) )
|
||||
|
||||
#define ROSH(REG,T,MASK) REG = (p = T[REG<<3 | p&MASK]) >> ksLSB
|
||||
#define ASL(REG) ROSH(REG, A2T.tROL, kfDV)
|
||||
#define LSR(REG) ROSH(REG, A2T.tROR, kfDV)
|
||||
#define ROL(REG) ROSH(REG, A2T.tROL, kfCDV)
|
||||
#define ROR(REG) ROSH(REG, A2T.tROR, kfCDV)
|
||||
|
||||
|
||||
// Miscellany:
|
||||
|
||||
#define CYCLES (mCycles + t + scanLine + (scanLine<<6))
|
||||
#define FLOATER zp[0x400 + t]
|
||||
// FLOATER not right yet!!
|
428
Source/LibAppleII/A2Computer/CPU-RW.h
Normal file
428
Source/LibAppleII/A2Computer/CPU-RW.h
Normal file
@ -0,0 +1,428 @@
|
||||
/* CPU-RW.h
|
||||
|
||||
Helper file that's included, twice, from "CPU.m": once for the read
|
||||
phase, and once for the write phase. This code implements reads and
|
||||
writes in the Apple II's $C0xx memory area.
|
||||
*/
|
||||
|
||||
#if READ_PHASE
|
||||
#define DONE goto Epilog
|
||||
#define DONE_F goto R_Floater8
|
||||
#define LABEL(NAME) R_##NAME
|
||||
|
||||
#else // write phase
|
||||
#define DONE goto NewOpcode
|
||||
#define DONE_F goto NewOpcode
|
||||
#define LABEL(NAME) W_##NAME
|
||||
#endif
|
||||
|
||||
#define CASE(N) case 0x##N + 1
|
||||
|
||||
#define VF_CASES(N, FLAGS) \
|
||||
CASE(N ): mFlags &= ~((FLAGS) & mMutableFlags); DONE_F; \
|
||||
CASE(N+1): mFlags |= ((FLAGS) & mMutableFlags); DONE_F;
|
||||
// for possibly changing video flags; and memory map not affected
|
||||
|
||||
#define MF_CASES(N, FLAGS) \
|
||||
CASE(N): \
|
||||
d = mFlags & ~((FLAGS) & mMutableFlags); goto LABEL(Remap); \
|
||||
CASE(N+1): \
|
||||
d = mFlags | ((FLAGS) & mMutableFlags); goto LABEL(Remap);
|
||||
// for possibly changing memory flags; memory map affected
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
switch ((ea ^ 0xC800) - 0x7FF)
|
||||
{
|
||||
enum
|
||||
{
|
||||
kfIOU = kfXYMASK | kfVBLMASK | kfX0EDGE | kfY0EDGE,
|
||||
kmLCEven = ~(kfLCBANK2 | kfLCRD | kfLCWRThi | kfLCWRTlo),
|
||||
kmHotSlot = ~(7UL << ksHotSlot),
|
||||
};
|
||||
|
||||
//-------------------------------------------------- $C00x, $C01x
|
||||
|
||||
#if READ_PHASE
|
||||
|
||||
CASE(00): CASE(01): CASE(02): CASE(03):
|
||||
CASE(04): CASE(05): CASE(06): CASE(07):
|
||||
CASE(08): CASE(09): CASE(0A): CASE(0B):
|
||||
CASE(0C): CASE(0D): CASE(0E): CASE(0F):
|
||||
if (mKeyQ.tail == mKeyQ.head) // then char queue is empty
|
||||
d = 0; // should be last char queued??
|
||||
else // queue has one or more characters
|
||||
d = 0x80 | mKeyQ.buf[mKeyQ.head];
|
||||
DONE;
|
||||
|
||||
CASE(10):
|
||||
if (mKeyQ.tail != mKeyQ.head)
|
||||
mKeyQ.head += 1;
|
||||
d = A2G.buttons << 3 & 0x80; // or'd with previous char!!
|
||||
DONE;
|
||||
|
||||
CASE(11): d = ksLCBANK2; goto R_Flag;
|
||||
CASE(12): d = ksLCRD; goto R_Flag;
|
||||
CASE(13): d = ksRAMRD; goto R_Flag;
|
||||
CASE(14): d = ksRAMWRT; goto R_Flag;
|
||||
CASE(15): d = ksCXROM; goto R_MuFlag;
|
||||
CASE(16): d = ksALTZP; goto R_Flag;
|
||||
CASE(17): d = ksC3ROM; goto R_MuFlag;
|
||||
CASE(18): d = ks80STOREv; goto R_Flag;
|
||||
|
||||
CASE(19): d = (scanLine >> 6) - 3; goto R_Floater7;
|
||||
|
||||
CASE(1A): d = ksTEXT; goto R_Flag;
|
||||
CASE(1B): d = ksMIXED; goto R_Flag;
|
||||
CASE(1C): d = ksPAGE2v; goto R_Flag;
|
||||
CASE(1D): d = ksHIRESv; goto R_Flag;
|
||||
CASE(1E): d = ksALTCHAR; goto R_Flag;
|
||||
CASE(1F): d = ks80COL; goto R_Flag;
|
||||
|
||||
#else // write phase
|
||||
|
||||
MF_CASES(00, kf80STOREm | kf80STOREv)
|
||||
MF_CASES(02, kfRAMRD)
|
||||
MF_CASES(04, kfRAMWRT)
|
||||
MF_CASES(06, kfCXROM)
|
||||
MF_CASES(08, kfALTZP)
|
||||
MF_CASES(0A, kfC3ROM)
|
||||
|
||||
VF_CASES(0C, kf80COL)
|
||||
VF_CASES(0E, kfALTCHAR)
|
||||
|
||||
CASE(10): CASE(11): CASE(12): CASE(13):
|
||||
CASE(14): CASE(15): CASE(16): CASE(17):
|
||||
CASE(18): CASE(19): CASE(1A): CASE(1B):
|
||||
CASE(1C): CASE(1D): CASE(1E): CASE(1F):
|
||||
if (mKeyQ.tail != mKeyQ.head)
|
||||
mKeyQ.head += 1;
|
||||
DONE;
|
||||
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------- $C02x, $C03x
|
||||
|
||||
CASE(20): CASE(21): CASE(22): CASE(23):
|
||||
CASE(24): CASE(25): CASE(26): CASE(27):
|
||||
CASE(28): CASE(29): CASE(2A): CASE(2B):
|
||||
CASE(2C): CASE(2D): CASE(2E): CASE(2F):
|
||||
if (mModel >= kA2ModelIIc)
|
||||
{
|
||||
d = mFlags ^ kfCXROM;
|
||||
goto LABEL(Remap);
|
||||
}
|
||||
if (not A2G.hearC02x)
|
||||
DONE_F;
|
||||
// else fall into C03x handler
|
||||
|
||||
CASE(30): CASE(31): CASE(32): CASE(33):
|
||||
CASE(34): CASE(35): CASE(36): CASE(37):
|
||||
CASE(38): CASE(39): CASE(3A): CASE(3B):
|
||||
CASE(3C): CASE(3D): CASE(3E): CASE(3F):
|
||||
{
|
||||
uint32_t *out, *delta;
|
||||
|
||||
d = ((t + 65*scanLine) * (kA2SamplesPerStep << 7)) / 17030;
|
||||
out = gSpkrOut + (d >> 7);
|
||||
delta = A2T.audio.delta + (d & 127);
|
||||
|
||||
out[0] = ~out[0] + delta[kFilterRes * 4];
|
||||
out[1] = ~out[1] + delta[kFilterRes * 3];
|
||||
out[2] = ~out[2] + delta[kFilterRes * 2];
|
||||
out[3] = ~out[3] + delta[kFilterRes];
|
||||
out[4] = (~out[4] + delta[0]) ^ 0xFF;
|
||||
|
||||
} DONE_F;
|
||||
|
||||
//-------------------------------------------------- $C04x, $C06x
|
||||
|
||||
#if READ_PHASE
|
||||
|
||||
CASE(40): d = ksXYMASK; goto R_Flag;
|
||||
CASE(41): d = ksVBLMASK; goto R_Flag;
|
||||
CASE(42): d = ksX0EDGE; goto R_Flag;
|
||||
CASE(43): d = ksY0EDGE; goto R_Flag;
|
||||
|
||||
CASE(60): CASE(68):
|
||||
d = 0; // cassette-in ??
|
||||
goto R_Floater7;
|
||||
|
||||
CASE(61): CASE(69):
|
||||
CASE(62): CASE(6A):
|
||||
CASE(63): CASE(6B):
|
||||
d = A2G.buttons << (-ea & 7);
|
||||
goto R_Floater7;
|
||||
|
||||
CASE(64): CASE(6C):
|
||||
CASE(65): CASE(6D):
|
||||
CASE(66): CASE(6E):
|
||||
CASE(67): CASE(6F):
|
||||
d = (CYCLES - mWhenC07x - A2G.paddle[ea&3]) >> 24;
|
||||
goto R_Floater7;
|
||||
|
||||
#else // write phase
|
||||
|
||||
CASE(40): CASE(41): CASE(42): CASE(43):
|
||||
CASE(60): CASE(61): CASE(62): CASE(63):
|
||||
CASE(64): CASE(65): CASE(66): CASE(67):
|
||||
CASE(68): CASE(69): CASE(6A): CASE(6B):
|
||||
CASE(6C): CASE(6D): CASE(6E): CASE(6F):
|
||||
// fall into DONE
|
||||
#endif
|
||||
|
||||
CASE(44): CASE(45): CASE(46): CASE(47):
|
||||
CASE(48): CASE(49): CASE(4A): CASE(4B):
|
||||
CASE(4C): CASE(4D): CASE(4E): CASE(4F):
|
||||
DONE;
|
||||
|
||||
//-------------------------------------------------- $C05x
|
||||
|
||||
CASE(50): mFlags &= ~kfTEXT; DONE_F;
|
||||
CASE(51): mFlags |= kfTEXT; DONE_F;
|
||||
CASE(52): mFlags &= ~kfMIXED; DONE_F;
|
||||
CASE(53): mFlags |= kfMIXED; DONE_F;
|
||||
|
||||
CASE(54): d = mFlags & ~(kfPAGE2m | kfPAGE2v); goto LABEL(Remap);
|
||||
CASE(55): d = mFlags | (kfPAGE2m | kfPAGE2v); goto LABEL(Remap);
|
||||
CASE(56): d = mFlags & ~(kfHIRESm | kfHIRESv); goto LABEL(Remap);
|
||||
CASE(57): d = mFlags | (kfHIRESm | kfHIRESv); goto LABEL(Remap);
|
||||
|
||||
VF_CASES(58, kfSINGRES | kfXYMASK)
|
||||
VF_CASES(5A, kfSINGRES | kfVBLMASK)
|
||||
VF_CASES(5C, kfSINGRES | kfX0EDGE)
|
||||
VF_CASES(5E, kfSINGRES | kfY0EDGE)
|
||||
|
||||
//-------------------------------------------------- $C07x
|
||||
|
||||
#if READ_PHASE
|
||||
|
||||
CASE(77): d = 0; // whether in graphics scanline!!
|
||||
goto R_Floater7;
|
||||
|
||||
CASE(7E): d = (mMutableFlags >> ksSINGRES) << 7;
|
||||
goto R_Floater7;
|
||||
|
||||
CASE(7F): d = ksSINGRES;
|
||||
goto R_Flag;
|
||||
|
||||
#else // write phase
|
||||
|
||||
CASE(7E): if (mModel >= kA2ModelIIc)
|
||||
mMutableFlags = mMutableFlags & ~kfIOU | kfSINGRES;
|
||||
goto LABEL(HitC07x);
|
||||
|
||||
CASE(7F): if (mModel >= kA2ModelIIc)
|
||||
mMutableFlags = mMutableFlags & ~kfSINGRES | kfIOU;
|
||||
goto LABEL(HitC07x);
|
||||
|
||||
CASE(77):
|
||||
// fall thru
|
||||
|
||||
#endif
|
||||
|
||||
CASE(70): CASE(71): CASE(72): CASE(73):
|
||||
CASE(74): CASE(75): CASE(76):
|
||||
CASE(78): CASE(79): CASE(7A): CASE(7B):
|
||||
CASE(7C): CASE(7D):
|
||||
LABEL(HitC07x):
|
||||
mWhenC07x = CYCLES;
|
||||
DONE_F;
|
||||
|
||||
//-------------------------------------------------- $C08x (lang card)
|
||||
|
||||
CASE(80):
|
||||
CASE(84): d = mFlags & kmLCEven | (kfLCBANK2 | kfLCRD);
|
||||
goto LABEL(Remap);
|
||||
CASE(82):
|
||||
CASE(86): d = mFlags & kmLCEven | kfLCBANK2;
|
||||
goto LABEL(Remap);
|
||||
CASE(88):
|
||||
CASE(8C): d = mFlags & kmLCEven | kfLCRD;
|
||||
goto LABEL(Remap);
|
||||
CASE(8A):
|
||||
CASE(8E): d = mFlags & kmLCEven;
|
||||
goto LABEL(Remap);
|
||||
|
||||
CASE(81):
|
||||
CASE(85): d = kfLCBANK2;
|
||||
goto LABEL(HitC08xOdd);
|
||||
CASE(83):
|
||||
CASE(87): d = kfLCBANK2 | kfLCRD;
|
||||
goto LABEL(HitC08xOdd);
|
||||
CASE(89):
|
||||
CASE(8D): d = 0;
|
||||
goto LABEL(HitC08xOdd);
|
||||
CASE(8B):
|
||||
CASE(8F): d = kfLCRD;
|
||||
goto LABEL(HitC08xOdd);
|
||||
|
||||
LABEL(HitC08xOdd):
|
||||
d |= mFlags & ~(kfLCBANK2 | kfLCRD);
|
||||
d += ~d >> 1 & kfLCWRTlo;
|
||||
goto LABEL(Remap);
|
||||
|
||||
//-------------------------------------------------- $C09x (printer)
|
||||
|
||||
#if READ_PHASE
|
||||
|
||||
CASE(90): CASE(91): CASE(92): CASE(93):
|
||||
CASE(94): CASE(95): CASE(96): CASE(97):
|
||||
CASE(98): CASE(99): CASE(9A): CASE(9B):
|
||||
CASE(9C): CASE(9D): CASE(9E): CASE(9F):
|
||||
d = mPrinter.reg[ea & 15];
|
||||
DONE;
|
||||
|
||||
#else // write phase
|
||||
|
||||
CASE(98): mPrinter.lights = kLightSustain | 1;
|
||||
putc(d & 0x7F, mPrinter.session);
|
||||
DONE;
|
||||
CASE(9A):
|
||||
CASE(9B): mPrinter.reg[ea & 15] = d;
|
||||
DONE;
|
||||
|
||||
CASE(90): CASE(91): CASE(92): CASE(93):
|
||||
CASE(94): CASE(95): CASE(96): CASE(97): CASE(99):
|
||||
CASE(9C): CASE(9D): CASE(9E): CASE(9F):
|
||||
DONE;
|
||||
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------- $C0Ax, $C0Bx
|
||||
|
||||
CASE(A0): CASE(A1): CASE(A2): CASE(A3):
|
||||
CASE(A4): CASE(A5): CASE(A6): CASE(A7):
|
||||
CASE(A8): CASE(A9): CASE(AA): CASE(AB):
|
||||
CASE(AC): CASE(AD): CASE(AE): CASE(AF):
|
||||
|
||||
CASE(B0): CASE(B1): CASE(B2): CASE(B3):
|
||||
CASE(B4): CASE(B5): CASE(B6): CASE(B7):
|
||||
CASE(B8): CASE(B9): CASE(BA): CASE(BB):
|
||||
CASE(BC): CASE(BD): CASE(BE):
|
||||
|
||||
DONE;
|
||||
|
||||
|
||||
CASE(BF):
|
||||
#if READ_PHASE
|
||||
d = A2T.curTime[mClock.index-- & 31];
|
||||
|
||||
#else // write phase
|
||||
mClock.index = d;
|
||||
|
||||
#endif
|
||||
DONE;
|
||||
|
||||
//-------------------------------------------------- $C0Cx (Slinky RAM)
|
||||
|
||||
#if READ_PHASE
|
||||
|
||||
CASE(C0): d = mSlinky.pos; DONE;
|
||||
CASE(C1): d = mSlinky.pos >> 8; DONE;
|
||||
CASE(C2): d = mSlinky.pos >> 16 | 0xF0; DONE;
|
||||
CASE(C3): d = mSlinky.base[mSlinky.pos++ & mSlinky.mask]; DONE;
|
||||
|
||||
CASE(CE): d = (mSlinky.mask + 1) >> 9; DONE;
|
||||
CASE(CF): d = (mSlinky.mask + 1) >> (9+8); DONE;
|
||||
|
||||
#else // write phase
|
||||
|
||||
CASE(C0): mSlinky.pos = mSlinky.pos & 0xFFFF00 | d; DONE;
|
||||
CASE(C1): mSlinky.pos = mSlinky.pos & 0xFF00FF | d<< 8; DONE;
|
||||
CASE(C2): mSlinky.pos = mSlinky.pos & 0x00FFFF | d<<16; DONE;
|
||||
CASE(C3): mSlinky.base[mSlinky.pos++ & mSlinky.mask] = d; DONE;
|
||||
|
||||
CASE(CE):
|
||||
CASE(CF): // fall thru
|
||||
|
||||
#endif
|
||||
|
||||
CASE(C4): CASE(C5): CASE(C6): CASE(C7):
|
||||
CASE(C8): CASE(C9): CASE(CA): CASE(CB):
|
||||
CASE(CC): CASE(CD):
|
||||
DONE_F;
|
||||
|
||||
//-------------------------------------------------- $C0D0-EF (IWMs)
|
||||
|
||||
CASE(D0): CASE(D1): CASE(D2): CASE(D3):
|
||||
CASE(D4): CASE(D5): CASE(D6): CASE(D7):
|
||||
CASE(D8): CASE(D9): CASE(DA): CASE(DB):
|
||||
CASE(DC): CASE(DD): CASE(DE): CASE(DF):
|
||||
|
||||
CASE(E0): CASE(E1): CASE(E2): CASE(E3):
|
||||
CASE(E4): CASE(E5): CASE(E6): CASE(E7):
|
||||
CASE(E8): CASE(E9): CASE(EA): CASE(EB):
|
||||
CASE(EC): CASE(ED): CASE(EE): CASE(EF):
|
||||
|
||||
#if READ_PHASE
|
||||
d = A2HitIWM(mIWM + (ea>>4 & 1), ea&31, 0);
|
||||
|
||||
#else // write phase
|
||||
A2HitIWM(mIWM + (ea>>4 & 1), ea|32, d);
|
||||
#endif
|
||||
DONE;
|
||||
|
||||
//-------------------------------------------------- $CFFF
|
||||
#if 0
|
||||
CASE(FF): //temp!!
|
||||
#if READ_PHASE
|
||||
d = mFlags >> ksHotSlot & 7;
|
||||
#else
|
||||
mFlags = d = (mFlags & kmHotSlot) | ((d&7) << ksHotSlot);
|
||||
REMAP_(d);
|
||||
#endif
|
||||
DONE;
|
||||
#endif
|
||||
|
||||
|
||||
case 0:
|
||||
#if 0
|
||||
if (mModel < kA2ModelIIc and (d = (pc>>8) ^ 0xC7) < 7)
|
||||
{
|
||||
mFlags = d = (mFlags & kmHotSlot) | ((d^7) << ksHotSlot);
|
||||
REMAP_(d);
|
||||
}
|
||||
#endif
|
||||
d = READ(ea);
|
||||
DONE;
|
||||
|
||||
//-------------------------------------------------- Misc epilogs
|
||||
|
||||
// Arrive at R_Remap or W_Remap with 'd' equal to the new value for
|
||||
// 'mFlags'. Arrive at R_Flag with 'd' equal to one of the 'ks'
|
||||
// flag constants. Arrive at R_Floater7 with 'd' having a significant
|
||||
// bit 7.
|
||||
|
||||
#if READ_PHASE
|
||||
|
||||
R_Remap: if (mFlags != d) {
|
||||
mFlags = d; REMAP_(d);
|
||||
}
|
||||
R_Floater8: d = FLOATER;
|
||||
DONE;
|
||||
|
||||
R_MuFlag: d = ((mFlags & mMutableFlags) >> d) << 7;
|
||||
goto R_Floater7;
|
||||
R_Flag: d = (mFlags >> d) << 7;
|
||||
R_Floater7: d = (d & 0x80) | (FLOATER & 0x7F);
|
||||
DONE;
|
||||
|
||||
#else // write phase
|
||||
|
||||
W_Remap: if (mFlags != d) {
|
||||
mFlags = d; REMAP_(d);
|
||||
}
|
||||
DONE;
|
||||
#endif
|
||||
|
||||
} // end of big switch on 'ea'
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#undef DONE
|
||||
#undef DONE_F
|
||||
#undef LABEL
|
||||
#undef CASE
|
||||
#undef VF_CASES
|
||||
#undef MF_CASES
|
629
Source/LibAppleII/A2Computer/CPU.m
Normal file
629
Source/LibAppleII/A2Computer/CPU.m
Normal file
@ -0,0 +1,629 @@
|
||||
/* class A2Computer (category CPU)
|
||||
|
||||
Routines and tables for emulating the 65c02 microprocessor, and the
|
||||
low-level I/O behavior of the Apple II.
|
||||
*/
|
||||
#import "LibAppleII-Priv.h"
|
||||
#import "CPU-Macros.h"
|
||||
//mport "CPU-Journal.h"
|
||||
|
||||
@implementation A2Computer (CPU)
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#if !JOURNALING
|
||||
#define JOURNAL_OP
|
||||
#define JOURNAL_EA
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
DFLAG(0, D)
|
||||
DFLAG(1, C)
|
||||
DFLAG(2, V)
|
||||
DFLAG(3, ZF) // when ZF = 1, forces Z = 1
|
||||
DFLAG(4, LSB)
|
||||
|
||||
kfCD = kfC | kfD,
|
||||
kfDV = kfD | kfV,
|
||||
kfCDV = kfC | kfDV,
|
||||
|
||||
kfN = 0x80 << ksLSB,
|
||||
kmZ8 = 0xFF << ksLSB,
|
||||
kmPHP = LENGTH(A2T.tPHP) - 1,
|
||||
|
||||
// kCyclesPerStep = 17030, // CPU cycles in one time step (262 * 65)
|
||||
};
|
||||
|
||||
static uint8_t gOldTimer, // residual cycles from last step
|
||||
gSpkrState; // speaker state: 0 or 0xFF
|
||||
|
||||
static uint32_t gSpkrOut[kA2SamplesPerStep + kTapRatio - 1];
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static void FillMemoryMapRow(int8_t map[0x81], uint32_t f, BOOL wmap)
|
||||
{/*
|
||||
Utility function used by +_InitCPU (below) to initialize one row of a
|
||||
memory mapping table -- either 'A2T.rmaps' or 'A2T.wmaps'.
|
||||
*/
|
||||
#define ORAM(BANK) \
|
||||
offsetof(A2Memory, RAM[page>>5][BANK][page<<8 & 0x1F00])
|
||||
|
||||
#define OROM(BANK) \
|
||||
offsetof(A2Memory, ROM[BANK][(page-0xC0)*256L])
|
||||
|
||||
#define PUT_PAGES(PGLO, PGHI, OFFSET) \
|
||||
for (int page = PGLO; page <= PGHI; page += 2) \
|
||||
map[page/2] = ((OFFSET) - ozp - 256L*page) / kChunkSize
|
||||
|
||||
enum { kChunkSize = 1L << 11, // = 0x800
|
||||
OWOM = offsetof(A2Memory, WOM),
|
||||
};
|
||||
int ramrw = f>>(wmap? ksRAMWRT : ksRAMRD) & 1,
|
||||
altzp = f>>ksALTZP & 1,
|
||||
cxrom = f>>ksCXROM & 1,
|
||||
hotSlot = f>>ksHotSlot & 7;
|
||||
long ozp = offsetof(A2Memory, RAM[0][altzp][0]);
|
||||
|
||||
map[0] = 0;
|
||||
map[0x80] = -(k64KB / kChunkSize);
|
||||
|
||||
PUT_PAGES(0x02, 0xBF, ORAM(ramrw));
|
||||
if (f & kf80STOREm)
|
||||
{
|
||||
int page2 = f>>ksPAGE2m & 1;
|
||||
|
||||
PUT_PAGES(0x04, 0x07, ORAM(page2));
|
||||
if (f & kfHIRESm)
|
||||
PUT_PAGES(0x20, 0x3F, ORAM(page2));
|
||||
}
|
||||
|
||||
if (wmap) // then setting write-map
|
||||
{
|
||||
PUT_PAGES(0xC0, 0xFF, OWOM);
|
||||
}
|
||||
else if (hotSlot == 0) // then setting read-map on IIc
|
||||
{
|
||||
PUT_PAGES(0xC0, 0xFF, OROM(cxrom));
|
||||
}
|
||||
else // setting read-map on IIe or earlier
|
||||
{
|
||||
int c3rom = f>>ksC3ROM & 1;
|
||||
BOOL c8_internal;
|
||||
|
||||
PUT_PAGES(0xD0, 0xFF, OROM(0));
|
||||
PUT_PAGES(0xC0, 0xC7, OROM(!cxrom));
|
||||
PUT_PAGES(0xC3, 0xC3, OROM(!cxrom & c3rom));
|
||||
|
||||
if (cxrom) // CXROM on, C3ROM irrelevant
|
||||
c8_internal = YES;
|
||||
else if (c3rom) // CXROM off, C3ROM on
|
||||
c8_internal = NO;
|
||||
else // CXROM and C3ROM both off
|
||||
c8_internal = YES; //!! (hotSlot == 3);
|
||||
|
||||
if (c8_internal)
|
||||
PUT_PAGES(0xC8, 0xCF, OROM(0));
|
||||
else
|
||||
PUT_PAGES(0xC8, 0xCF, OROM(1) + 0x800*(hotSlot-1));
|
||||
}
|
||||
|
||||
if (f & (wmap? kfLCWRThi : kfLCRD)) // then $D0-FF sees LC RAM
|
||||
{
|
||||
int shiftDx = (f & kfLCBANK2)? 0x1000 : 0;
|
||||
|
||||
PUT_PAGES(0xD0, 0xDF, ORAM(altzp) - shiftDx);
|
||||
PUT_PAGES(0xE0, 0xFF, ORAM(altzp));
|
||||
}
|
||||
|
||||
#undef ORAM
|
||||
#undef OROM
|
||||
#undef PUT_PAGES
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static int16_t FixAP(uint16_t oap)
|
||||
{
|
||||
unsigned p = kfLSB;
|
||||
|
||||
if (oap & 0x80) p |= 0x80;
|
||||
if (oap & 0x40) p |= kfV;
|
||||
if (oap & 0x08) p |= kfD;
|
||||
if (oap & 0x02) p |= kfZF;
|
||||
if (oap & 0x01) p |= kfC;
|
||||
|
||||
return (p << 8) | (oap >> 8);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static BOOL InitADC_SBC(void)
|
||||
{
|
||||
// Fill in the ADC and SBC lookup tables in global structure 'A2T'.
|
||||
|
||||
static uint8_t tbl
|
||||
[2/*D*/][2/*C*/][256/*B*/][2/*ADC,SBC*/][2/*A,P*/][256/*A*/];
|
||||
static uint16_t tadc[0xD00], tsbc[0xD00];
|
||||
BOOL adc_good = YES, sbc_good = YES;
|
||||
FILE* fin;
|
||||
|
||||
fin = fopen([[[[NSBundle mainBundle] bundlePath]
|
||||
stringByAppendingPathComponent:@"../ADSBC.dat"]
|
||||
fileSystemRepresentation], "rb");
|
||||
if (fin == NULL)
|
||||
return NO;
|
||||
|
||||
fread(tbl, 1, sizeof(tbl), fin);
|
||||
fclose(fin);
|
||||
memset(tadc, 0xFF, sizeof(tadc));
|
||||
memset(tsbc, 0xFF, sizeof(tsbc));
|
||||
|
||||
for (int i = 256; --i >= 0;)
|
||||
tadc[i] = tsbc[i] = 128 + 2 *
|
||||
( (i & 0x80)*15/4 + (i & 0x70)*2 + (i & 0xF) );
|
||||
|
||||
for (int d = 2; --d >= 0;)
|
||||
for (int c = 2; --c >= 0;)
|
||||
for (int b = 256; --b >= 0;)
|
||||
for (int a = 256; --a >= 0;)
|
||||
{
|
||||
unsigned adc, sbc, i;
|
||||
|
||||
adc = tbl[d][c][b][0][0][a] << 8
|
||||
| tbl[d][c][b][0][1][a] & 0xCB;
|
||||
sbc = tbl[d][c][b][1][0][a] << 8
|
||||
| tbl[d][c][b][1][1][a] & 0xCB;
|
||||
|
||||
i = tadc[a] + tadc[b] + 2*c + d;
|
||||
if (tadc[i] == 0xFFFF)
|
||||
tadc[i] = adc;
|
||||
else if (tadc[i] != adc)
|
||||
adc_good = NO;
|
||||
|
||||
i = tsbc[a] + tsbc[b^0xFF] + 2*c + d;
|
||||
if (tsbc[i] == 0xFFFF)
|
||||
tsbc[i] = sbc;
|
||||
else if (tsbc[i] != sbc)
|
||||
sbc_good = NO;
|
||||
}
|
||||
|
||||
NSLog(@"adc_good? %c sbc_good? %c", "ny"[adc_good], "ny"[sbc_good]);
|
||||
|
||||
if (adc_good and sbc_good)
|
||||
{
|
||||
memcpy(A2T.tADC, tadc, 2*256);
|
||||
memcpy(A2T.tSBC, tsbc, 2*256);
|
||||
|
||||
for (int i = 256; i < LENGTH(A2T.tADC); ++i)
|
||||
{
|
||||
A2T.tADC[i] = FixAP(tadc[i]);
|
||||
A2T.tSBC[i] = FixAP(tsbc[i]);
|
||||
}
|
||||
|
||||
memcpy(A2T.tADCo, A2T.tADC, sizeof(A2T.tADC));
|
||||
memcpy(A2T.tSBCo, A2T.tSBC, sizeof(A2T.tSBC));
|
||||
|
||||
if (NO) for (int i = 256; i < LENGTH(A2T.tADCo); i += 2)
|
||||
{
|
||||
A2T.tADCo[i+1] =
|
||||
A2T.tADCo[i ] & 0xFF00 |
|
||||
A2T.tADCo[i+1] & 0x00FF;
|
||||
|
||||
A2T.tSBCo[i+1] =
|
||||
A2T.tSBCo[i ] & 0xFF00 |
|
||||
A2T.tSBCo[i+1] & 0x00FF;
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
+ (void)_InitCPU
|
||||
{/*
|
||||
Initializes various lookup tables used in method '-RunForOneStep',
|
||||
defined below. Called only once, from '+initialize'.
|
||||
*/
|
||||
|
||||
#if JOURNALING
|
||||
atexit(LogJournal);
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
for (int p = LENGTH(A2T.tPHP); --p >= 0;)
|
||||
{
|
||||
uint8_t php = 0x30; // the true P reg: NV1BDIZC
|
||||
|
||||
if (p & kfC) php |= 0x01;
|
||||
if (Z_SET) php |= 0x02;
|
||||
if (p & kfD) php |= 0x08;
|
||||
if (p & kfV) php |= 0x40;
|
||||
if (p & kfN) php |= 0x80;
|
||||
|
||||
A2T.tPHP[p] = php;
|
||||
}
|
||||
|
||||
for (int i = LENGTH(A2T.tPLP); --i >= 0;)
|
||||
{
|
||||
unsigned plp = kfLSB; // assume Z = 0 (datum non-zero)
|
||||
|
||||
if (i & 0x01) plp |= kfC;
|
||||
if (i & 0x02) plp |= kfZF;
|
||||
if (i & 0x08) plp |= kfD;
|
||||
if (i & 0x40) plp |= kfV;
|
||||
if (i & 0x80) plp |= kfN;
|
||||
|
||||
A2T.tPLP[i] = plp;
|
||||
}
|
||||
|
||||
for (long i = LENGTH(A2T.tROR), j; --i >= 0;)
|
||||
{
|
||||
j = i>>3 | (i&kfC)<<7;
|
||||
j = (j<<9 | j) << 3;
|
||||
A2T.tROR[i] = j&0xFF0 | i&kfDV | j>>2&kfC;
|
||||
j >>= 7;
|
||||
A2T.tROL[i] = j&0xFF0 | i&kfDV | j>>2&kfC;
|
||||
}
|
||||
|
||||
InitADC_SBC();
|
||||
|
||||
for (long i = kmRMap+1; --i >= 0;)
|
||||
FillMemoryMapRow(A2T.rmaps[i], i<<ksRMap, NO);
|
||||
for (long i = kmWMap+1; --i >= 0;)
|
||||
FillMemoryMapRow(A2T.wmaps[i], i<<ksWMap, YES);
|
||||
|
||||
A2DumpArray("tADC", A2T.tADC, sizeof(A2T.tADC), -2);
|
||||
A2DumpArray("tSBC", A2T.tSBC, sizeof(A2T.tSBC), -2);
|
||||
A2DumpArray("tADCo", A2T.tADCo, sizeof(A2T.tADCo), -2);
|
||||
A2DumpArray("tSBCo", A2T.tSBCo, sizeof(A2T.tSBCo), -2);
|
||||
A2DumpArray("tROL", A2T.tROL, sizeof(A2T.tROL), 2);
|
||||
A2DumpArray("tROR", A2T.tROR, sizeof(A2T.tROR), 2);
|
||||
A2DumpArray("tPLP", A2T.tPLP, sizeof(A2T.tPLP), 2);
|
||||
A2DumpArray("tPHP", A2T.tPHP, sizeof(A2T.tPHP), 1);
|
||||
A2DumpArray("rmaps", A2T.rmaps, sizeof(A2T.rmaps), -1);
|
||||
A2DumpArray("wmaps", A2T.wmaps, sizeof(A2T.wmaps), -1);
|
||||
#endif
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)RunForOneStep:(uint8_t [])audioOut
|
||||
{/*
|
||||
Executes 65c02 instructions for one time step: about 17,030 CPU cyles,
|
||||
or 1/60th of an emulated second. Also computes the 8-bit audio waveform
|
||||
for the time step and writes it to the array 'audioOut'.
|
||||
|
||||
No attempt is made here to keep emulation time in sync with real time.
|
||||
It's up to the library user to call this method every 60th of a second,
|
||||
and keep the audio stream playing.
|
||||
*/
|
||||
if (mHalts) // != 0, then emulation is forbidden just now
|
||||
{
|
||||
[self _DefaultAudio:audioOut:kA2SamplesPerStep];
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned p = mP;
|
||||
unsigned pc = mPC;
|
||||
int32_t t = gOldTimer;
|
||||
uint8_t *zp;
|
||||
int8_t *rmap, *wmap;
|
||||
|
||||
REMAP_(mFlags);
|
||||
mCycles += 17030; // CPU cycles per step
|
||||
|
||||
for (int scanLine = 0; scanLine < 262;)
|
||||
{
|
||||
uint32_t d;
|
||||
unsigned ea;
|
||||
uint8_t curOp;
|
||||
|
||||
mVideoFlags[scanLine++] = mFlags;
|
||||
t -= 65;
|
||||
--pc;
|
||||
|
||||
//----------------------------------------------------------------
|
||||
NewOpcode:
|
||||
|
||||
d = READ_PC; // = next opcode to interpret
|
||||
JOURNAL_OP
|
||||
|
||||
switch (t >> 7 & d)
|
||||
{
|
||||
/*--------------------------------------------------------
|
||||
BRK, JSR, JMP, RTI, and RTS.
|
||||
*/
|
||||
#define PHPC2 ea = pc+2; PUSH(ea>>8); PUSH(ea)
|
||||
#define PLPC pc = PULL; pc |= PULL<<8
|
||||
|
||||
case 0x00: if (t >= 0)
|
||||
continue; // next scan line
|
||||
/* BRK */ PHPC2; PHP;
|
||||
mI = 4; ea = 0xFFFE; ++t;
|
||||
goto IndirJMP;
|
||||
|
||||
case 0x7C: EAAX; goto IndirJMP;
|
||||
case 0x6C: EAA; // and fall into IndirJMP
|
||||
IndirJMP: pc = READ(ea) - 1; ++ea; t += 6;
|
||||
pc += READ(ea) << 8;
|
||||
goto NewOpcode;
|
||||
|
||||
case 0x20: PHPC2; t += 3; // and fall into JMP-abs
|
||||
case 0x4C: EAA; pc = ea - 1; t += 3; goto NewOpcode;
|
||||
|
||||
case 0x60: PLPC; t += 6; goto NewOpcode;
|
||||
case 0x40: PLP; PLPC; --pc; t += 6; goto NewOpcode;
|
||||
|
||||
#undef PHPC2
|
||||
#undef PLPC
|
||||
|
||||
/*--------------------------------------------------------
|
||||
No-op instructions.
|
||||
*/
|
||||
case 0x54: case 0xD4: case 0xF4: ++t;
|
||||
case 0x44: ++t;
|
||||
case 0x02: case 0x22: case 0x42:
|
||||
case 0x62: case 0x82: case 0xC2:
|
||||
case 0xE2: ++pc;
|
||||
case 0xEA: t += 2; goto NewOpcode;
|
||||
|
||||
case 0x5C: t += 4;
|
||||
case 0xDC: case 0xFC: t += 4; pc += 2; goto NewOpcode;
|
||||
|
||||
default:
|
||||
CASE16(0x03,16): CASE16(0x07,16):
|
||||
CASE16(0x0B,16): CASE16(0x0F,16): ++t; goto NewOpcode;
|
||||
|
||||
/*--------------------------------------------------------
|
||||
Relative branches.
|
||||
*/
|
||||
#define BRANCHES(OP, COND) \
|
||||
case OP: if (COND) goto DoBRA; \
|
||||
++pc; t += 2; goto NewOpcode; \
|
||||
case OP ^ 0x20: if (not (COND)) goto DoBRA; \
|
||||
++pc; t += 2; goto NewOpcode;
|
||||
|
||||
BRANCHES(0xB0, p & kfC) // BCS & BCC
|
||||
BRANCHES(0x70, p & kfV) // BVS & BVC
|
||||
BRANCHES(0x30, p & kfN) // BMI & BPL
|
||||
BRANCHES(0xF0, Z_SET ) // BEQ & BNE
|
||||
|
||||
DoBRA: case 0x80:
|
||||
pc += (signed char) READ_PC;
|
||||
t += 3;
|
||||
goto NewOpcode;
|
||||
|
||||
#undef BRANCHES
|
||||
|
||||
/*--------------------------------------------------------
|
||||
Implied- and Accumulator-mode instructions:
|
||||
*/
|
||||
#define IMPI(OP, DT, STMT) \
|
||||
case OP: t += DT; STMT; goto NewOpcode;
|
||||
#define LD_(REG,VAL) REG = d = (VAL); SET_NZ
|
||||
|
||||
IMPI(0xB8, 2, p &= ~kfV)
|
||||
IMPI(0x18, 2, p &= ~kfC) IMPI(0x38, 2, p |= kfC)
|
||||
IMPI(0xD8, 2, p &= ~kfD) IMPI(0xF8, 2, p |= kfD)
|
||||
IMPI(0x58, 2, mI = 0) IMPI(0x78, 2, mI = 4)
|
||||
|
||||
IMPI(0x1A, 2, LD_(mA, mA+1)) IMPI(0x3A, 2, LD_(mA, mA-1))
|
||||
IMPI(0xE8, 2, LD_(mX, mX+1)) IMPI(0xCA, 2, LD_(mX, mX-1))
|
||||
IMPI(0xC8, 2, LD_(mY, mY+1)) IMPI(0x88, 2, LD_(mY, mY-1))
|
||||
|
||||
IMPI(0x8A, 2, LD_(mA, mX)) IMPI(0xAA, 2, LD_(mX, mA))
|
||||
IMPI(0x98, 2, LD_(mA, mY)) IMPI(0xA8, 2, LD_(mY, mA))
|
||||
IMPI(0xBA, 2, LD_(mX, mS)) IMPI(0x9A, 2, mS = mX)
|
||||
|
||||
IMPI(0x08, 3, PHP) IMPI(0x28, 4, PLP)
|
||||
IMPI(0x48, 3, PUSH(mA)) IMPI(0x68, 4, LD_(mA, PULL))
|
||||
IMPI(0xDA, 3, PUSH(mX)) IMPI(0xFA, 4, LD_(mX, PULL))
|
||||
IMPI(0x5A, 3, PUSH(mY)) IMPI(0x7A, 4, LD_(mY, PULL))
|
||||
|
||||
IMPI(0x0A, 2, ASL(mA)) IMPI(0x4A, 2, LSR(mA))
|
||||
IMPI(0x2A, 2, ROL(mA)) IMPI(0x6A, 2, ROR(mA))
|
||||
|
||||
#undef IMPI
|
||||
#undef LD_
|
||||
|
||||
/*--------------------------------------------------------
|
||||
Read and modify instructions of the Immediate and
|
||||
Zero-Page addressing modes.
|
||||
*/
|
||||
#define RIMM(OP, STMT) case 0x##OP: \
|
||||
d = READ_PC; t += 2; STMT; goto NewOpcode;
|
||||
#define RZP(OP, STMT) case 0x##OP: \
|
||||
EAZ; t += 3; d = zp[ea]; STMT; goto NewOpcode;
|
||||
#define RZPX(OP, STMT) case 0x##OP: \
|
||||
EAZX; t += 4; d = zp[ea]; STMT; goto NewOpcode;
|
||||
|
||||
#define MZP(OP, STMT) case 0x##OP: \
|
||||
EAZ; t+=5; d=zp[ea]; STMT; zp[ea]=d; goto NewOpcode;
|
||||
#define MZPX(OP, STMT) case 0x##OP: \
|
||||
EAZX; t+=6; d=zp[ea]; STMT; zp[ea]=d; goto NewOpcode;
|
||||
|
||||
RIMM(69, ADC) RZP(65, ADC) RZPX(75, ADC)
|
||||
RIMM(29, AND) RZP(25, AND) RZPX(35, AND)
|
||||
MZP(06, ASL(d)) MZPX(16, ASL(d))
|
||||
RIMM(89, BITZ) RZP(24, BIT) RZPX(34, BIT)
|
||||
RIMM(C9, CMP) RZP(C5, CMP) RZPX(D5, CMP)
|
||||
RIMM(E0, CPX) RZP(E4, CPX)
|
||||
RIMM(C0, CPY) RZP(C4, CPY)
|
||||
MZP(C6, DEC) MZPX(D6, DEC)
|
||||
RIMM(49, EOR) RZP(45, EOR) RZPX(55, EOR)
|
||||
MZP(E6, INC) MZPX(F6, INC)
|
||||
RIMM(A9, LDA) RZP(A5, LDA) RZPX(B5, LDA)
|
||||
RIMM(A2, LDX) RZP(A6, LDX)
|
||||
RIMM(A0, LDY) RZP(A4, LDY) RZPX(B4, LDY)
|
||||
MZP(46, LSR(d)) MZPX(56, LSR(d))
|
||||
RIMM(09, ORA) RZP(05, ORA) RZPX(15, ORA)
|
||||
MZP(26, ROL(d)) MZPX(36, ROL(d))
|
||||
MZP(66, ROR(d)) MZPX(76, ROR(d))
|
||||
RIMM(E9, SBC) RZP(E5, SBC) RZPX(F5, SBC)
|
||||
MZP(14, TRB)
|
||||
MZP(04, TSB)
|
||||
|
||||
case 0xB6: // LDX zp,Y
|
||||
EAZY; t += 4; d = zp[ea]; LDX; goto NewOpcode;
|
||||
|
||||
#undef RIMM
|
||||
#undef RZP
|
||||
#undef RZPX
|
||||
#undef MZP
|
||||
#undef MZPX
|
||||
|
||||
/*--------------------------------------------------------
|
||||
STA, STX, STY, and STZ
|
||||
*/
|
||||
case 0x85: EAZ ; t += 3; zp[ea] = mA; goto NewOpcode;
|
||||
case 0x86: EAZ ; t += 3; zp[ea] = mX; goto NewOpcode;
|
||||
case 0x84: EAZ ; t += 3; zp[ea] = mY; goto NewOpcode;
|
||||
case 0x64: EAZ ; t += 3; zp[ea] = 0; goto NewOpcode;
|
||||
|
||||
case 0x95: EAZX; t += 4; zp[ea] = mA; goto NewOpcode;
|
||||
case 0x96: EAZY; t += 4; zp[ea] = mX; goto NewOpcode;
|
||||
case 0x94: EAZX; t += 4; zp[ea] = mY; goto NewOpcode;
|
||||
case 0x74: EAZX; t += 4; zp[ea] = 0; goto NewOpcode;
|
||||
|
||||
case 0x8D: EAA ; t += 4; d = mA; goto Write;
|
||||
case 0x8E: EAA ; t += 4; d = mX; goto Write;
|
||||
case 0x8C: EAA ; t += 4; d = mY; goto Write;
|
||||
case 0x9C: EAA ; t += 4; d = 0; goto Write;
|
||||
|
||||
case 0x9D: EAAX; t += 5; d = mA; goto Write;
|
||||
case 0x99: EAAY; t += 5; d = mA; goto Write;
|
||||
case 0x92: EAI ; t += 5; d = mA; goto Write;
|
||||
case 0x81: EAIX; t += 6; d = mA; goto Write;
|
||||
case 0x91: EAIY; t += 6; d = mA; goto Write;
|
||||
|
||||
case 0x9E: EAAX; t += 5; d = 0; goto Write;
|
||||
|
||||
/*--------------------------------------------------------
|
||||
"Prologs" for read and modify instructions that work
|
||||
on general addresses. The effective address is
|
||||
computed, and the clock is bumped. Execution then
|
||||
proceeds to the Read and Epilog phases below.
|
||||
*/
|
||||
case 0x6D: case 0x2D: case 0x0E: case 0x2C: case 0xCD:
|
||||
case 0xEC: case 0xCC: case 0xCE: case 0x4D: case 0xEE:
|
||||
case 0xAD: case 0xAE: case 0xAC: case 0x4E: case 0x0D:
|
||||
case 0x2E: case 0x6E: case 0xED: case 0x1C: case 0x0C:
|
||||
EAA; t += 4; break;
|
||||
|
||||
case 0x7D: case 0x3D: case 0x1E: case 0x3C: case 0xDD:
|
||||
case 0xDE: case 0x5D: case 0xFE: case 0xBD: case 0xBC:
|
||||
case 0x5E: case 0x1D: case 0x3E: case 0x7E: case 0xFD:
|
||||
EAAX; t += 4; break;
|
||||
|
||||
case 0x79: case 0x39: case 0xD9: case 0x59: case 0xB9:
|
||||
case 0xBE: case 0x19: case 0xF9:
|
||||
EAAY; t += 4; break;
|
||||
|
||||
case 0x72: case 0x32: case 0xD2: case 0x52: case 0xB2:
|
||||
case 0x12: case 0xF2:
|
||||
EAI; t += 5; break;
|
||||
|
||||
case 0x61: case 0x21: case 0xC1: case 0x41: case 0xA1:
|
||||
case 0x01: case 0xE1:
|
||||
EAIX; t += 6; break;
|
||||
|
||||
case 0x71: case 0x31: case 0xD1: case 0x51: case 0xB1:
|
||||
case 0x11: case 0xF1:
|
||||
EAIY; t += 5; break;
|
||||
|
||||
} // end of switch (t>>16 & d)
|
||||
|
||||
//----------------------------------------------------------------
|
||||
Read:
|
||||
|
||||
curOp = d;
|
||||
JOURNAL_EA
|
||||
|
||||
#define READ_PHASE 1
|
||||
#include "CPU-RW.h"
|
||||
#undef READ_PHASE
|
||||
|
||||
d = READ(ea); // default read behavior, when 'ea' not in I/O area
|
||||
// fall into Epilog...
|
||||
|
||||
//----------------------------------------------------------------
|
||||
Epilog:
|
||||
|
||||
switch (curOp)
|
||||
{
|
||||
#define OP_ACC(OP, STMT) case OP+0x12: \
|
||||
case OP+0x01: case OP+0x05: case OP+0x09: case OP+0x0D: \
|
||||
case OP+0x11: case OP+0x15: case OP+0x19: case OP+0x1D: \
|
||||
STMT; goto NewOpcode;
|
||||
|
||||
OP_ACC(0x00, ORA) OP_ACC(0x20, AND) OP_ACC(0x40, EOR)
|
||||
OP_ACC(0x60, ADC) OP_ACC(0xA0, LDA) OP_ACC(0xC0, CMP)
|
||||
OP_ACC(0xE0, SBC)
|
||||
|
||||
case 0x2C: case 0x3C: BIT; goto NewOpcode;
|
||||
case 0xEC: CPX; goto NewOpcode;
|
||||
case 0xCC: CPY; goto NewOpcode;
|
||||
case 0xAE: case 0xBE: LDX; goto NewOpcode;
|
||||
case 0xAC: case 0xBC: LDY; goto NewOpcode;
|
||||
|
||||
case 0x0E: case 0x1E: ASL(d); break;
|
||||
case 0x4E: case 0x5E: LSR(d); break;
|
||||
case 0x2E: case 0x3E: ROL(d); break;
|
||||
case 0x6E: case 0x7E: ROR(d); break;
|
||||
case 0xCE: case 0xDE: DEC; break;
|
||||
case 0xEE: case 0xFE: INC; break;
|
||||
case 0x0C: TSB; break;
|
||||
case 0x1C: TRB; break;
|
||||
|
||||
case 0x4C: case 0x5C: case 0x6C: case 0x7C: case 0x8C:
|
||||
case 0x9C: case 0xDC: case 0xFC: case 0x8E: case 0x9E:
|
||||
CASE16(0x00, 0x10): CASE8 (0x02, 0x20): CASE16(0x03, 0x10):
|
||||
CASE16(0x04, 0x10): CASE16(0x06, 0x10): CASE16(0x07, 0x10):
|
||||
CASE16(0x08, 0x10): CASE16(0x0A, 0x10): CASE16(0x0B, 0x10):
|
||||
CASE16(0x0F, 0x10): default:
|
||||
OP_ACC(0x80, ;) // goto NewOpcode
|
||||
|
||||
#undef OP_ACC
|
||||
}
|
||||
|
||||
// Modify instructions reach here. We need to burn 2 more
|
||||
// cycles before falling into Write.
|
||||
t += 2;
|
||||
|
||||
//----------------------------------------------------------------
|
||||
Write:
|
||||
|
||||
#include "CPU-RW.h"
|
||||
|
||||
JOURNAL_EA
|
||||
WRITE(ea) = d; // default write, when 'ea' not in I/O area
|
||||
goto NewOpcode;
|
||||
|
||||
} // end of for (scanLine ...)
|
||||
|
||||
|
||||
mPC = pc;
|
||||
mP = p;
|
||||
gOldTimer = t;
|
||||
|
||||
p = gSpkrState;
|
||||
for (int i = 0; i < kA2SamplesPerStep; ++i)
|
||||
{
|
||||
t = gSpkrOut[i];
|
||||
p ^= t>>8;
|
||||
audioOut[i] = p ^ t>>24;
|
||||
p ^= t;
|
||||
}
|
||||
gSpkrState = p;
|
||||
|
||||
for (int i = kTapRatio-1; --i >= 0;)
|
||||
gSpkrOut[i] = (gSpkrOut + kA2SamplesPerStep)[i];
|
||||
t = A2T.audio.flat;
|
||||
for (int i = kA2SamplesPerStep; --i >= 0;)
|
||||
(gSpkrOut + kTapRatio - 1)[i] = t;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
224
Source/LibAppleII/A2Computer/Printing.l
Normal file
224
Source/LibAppleII/A2Computer/Printing.l
Normal file
@ -0,0 +1,224 @@
|
||||
/* class A2Computer (category Printing)
|
||||
*/
|
||||
%option 7bit never-interactive noyywrap prefix="A2Printing_"
|
||||
%x EpsonRX
|
||||
|
||||
%{ --------------------------------------------------------------------------
|
||||
#import "LibAppleII-Priv.h"
|
||||
|
||||
#define YY_DECL static void yylex(unsigned filter)
|
||||
|
||||
static int input(void);
|
||||
//static uint8_t gEpsonFontForStyle[0x2FF + 1];
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static void EpsonText(BOOL trailingRubouts)
|
||||
{
|
||||
int n = yyleng;
|
||||
|
||||
fputs("\n(--", yyout);
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
char ch = yytext[i];
|
||||
|
||||
if (ch == '(' or ch == ')' or ch == '\\')
|
||||
fputc('\\', yyout);
|
||||
fputc(ch, yyout);
|
||||
}
|
||||
fputs(") T", yyout);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static void EpsonGraphics(int mode)
|
||||
{
|
||||
const char* hexDigit = "0123456789ABCDEF";
|
||||
int nbytes,
|
||||
data;
|
||||
|
||||
nbytes = (yytext[yyleng-1]&7)<<8 | yytext[yyleng-2];
|
||||
if (nbytes < 1)
|
||||
return;
|
||||
|
||||
fputs("\n<7F80 ", yyout);
|
||||
while (--nbytes >= 0 and EOF != (data = input()))
|
||||
{
|
||||
putc(hexDigit[data >> 4], yyout);
|
||||
putc(hexDigit[data & 15], yyout);
|
||||
}
|
||||
fprintf(yyout, "> G%c", mode | '0');
|
||||
}
|
||||
|
||||
%} --------------------------------------------------------------------------
|
||||
|
||||
ESC \x1B
|
||||
ANY [\0-\x7F]
|
||||
ANY2 {ANY}{2}
|
||||
ANY3 {ANY}{3}
|
||||
|
||||
%%
|
||||
%{
|
||||
unsigned charset = 0;
|
||||
BOOL gcharset = NO; // Epson-specific state
|
||||
|
||||
yyrestart(yyin);
|
||||
BEGIN(filter);
|
||||
%}
|
||||
|
||||
<EpsonRX>{
|
||||
<<EOF>> fputs("\nshowpage\n", yyout); return;
|
||||
{ESC}[@] { fputs("\nReset\n", yyout);
|
||||
charset = 0; gcharset = NO;
|
||||
}
|
||||
{ESC}R[\0-\12] charset = yytext[2];
|
||||
{ESC}m[\0\4] gcharset = yytext[2] >> 2;
|
||||
{ESC}[<] fputs(" CR", yyout);
|
||||
|
||||
{ESC}K{ANY2} EpsonGraphics(0);
|
||||
{ESC}L{ANY2} EpsonGraphics(1);
|
||||
{ESC}Y{ANY2} EpsonGraphics(2);
|
||||
{ESC}Z{ANY2} EpsonGraphics(3);
|
||||
{ESC}[*][\0-\4\6]{ANY2} EpsonGraphics(yytext[2]);
|
||||
|
||||
{ESC}[01245EFGHMOPT] {
|
||||
fprintf(yyout, " E-%c", yytext[1]);
|
||||
}
|
||||
{ESC}?[\x0F\x12\x0E\x14] {
|
||||
fprintf(yyout, " C-%c", yytext[yyleng-1] | '@');
|
||||
}
|
||||
{ESC}[-SW][01\0\1] {
|
||||
fprintf(yyout, " E-%c%c", yytext[1], yytext[2] | '0');
|
||||
}
|
||||
{ESC}C\0{ANY} |
|
||||
{ESC}[ef][01]{ANY} {
|
||||
fprintf(yyout, " %d E-%c%c", yytext[3], yytext[1], yytext[2]|'0');
|
||||
}
|
||||
{ESC}[3ACJNQl]{ANY} {
|
||||
fprintf(yyout, " %d E-%c", yytext[2], yytext[1]);
|
||||
}
|
||||
|
||||
\x08+|\x09+|\x0A+|\x0B+|\x0C+|\x0D+ {
|
||||
fprintf(yyout, " %d C-%c", yyleng, yytext[0] | '@');
|
||||
}
|
||||
|
||||
[\x20-\x7E]+\x18+ ;
|
||||
[\x20-\x7E]+\x7F+ EpsonText(YES);
|
||||
[\x20-\x7E]+ EpsonText(NO);
|
||||
|
||||
{ESC}[*]{ANY3} |
|
||||
{ESC}[ef]{ANY2} |
|
||||
{ESC}[-SRUWms]{ANY} |
|
||||
{ESC}{ANY} ; // invalid sequences, skipped
|
||||
}
|
||||
|
||||
<INITIAL>{
|
||||
[\r\n\t\v\f]+ |
|
||||
[\x20-\x7E]+ ECHO;
|
||||
}
|
||||
|
||||
<*>{
|
||||
{ANY} ; // ignored if not handled above
|
||||
<<EOF>> return;
|
||||
}
|
||||
|
||||
%%
|
||||
@implementation A2Computer (Printing)
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
+ (void)_InitPrinting
|
||||
{
|
||||
#if 0
|
||||
enum
|
||||
{
|
||||
// Epson text-style flags:
|
||||
|
||||
kEpItalics = 1, kEpCompressed = 1<<4,
|
||||
kEpUnderline = 1<<1, kEpEmphasized = 1<<5,
|
||||
kEpExpanded = 1<<2, kEpElite = 1<<6,
|
||||
kEpExpanded1L = 1<<3, kEpDblStrike = 1<<7,
|
||||
|
||||
kEpSubscript = 1<<8, // at most one Script bit is ever 1
|
||||
kEpSuperscript = 1<<9,
|
||||
};
|
||||
|
||||
// Set up the mapping from Epson style flags to font numbers.
|
||||
|
||||
for (int style = sizeof(gEpsonFontForStyle); --style >= 0;)
|
||||
{
|
||||
unsigned f = style & (kEpItalics | kEpUnderline);
|
||||
|
||||
if (style & (kEpExpanded | kEpExpanded1L))
|
||||
f |= 4;
|
||||
f |= "\x00\x18\x08\x08\x10\x10\x10\x10"[style>>4 & 7];
|
||||
f |= "\x00\x20\x40\x40\x60\x60"[style>>7];
|
||||
|
||||
gEpsonFontForStyle[style] = f;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (long)SizeOfPrintSession
|
||||
{/*
|
||||
Returns the number of bytes that have accumulated so far in the
|
||||
printer session.
|
||||
*/
|
||||
fflush(mPrinter.session); // (probably unnecessary)
|
||||
return ftell(mPrinter.session);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)ClearPrintSession
|
||||
{/*
|
||||
Clears (purges) the printer session. All data bytes that this printer
|
||||
has received are discarded.
|
||||
*/
|
||||
fflush(mPrinter.session);
|
||||
fseek(mPrinter.session, 0, SEEK_SET);
|
||||
ftruncate(fileno(mPrinter.session), 0);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (BOOL)SavePrintSessionAs:(unsigned)filter toFile:(NSString*)fpath
|
||||
{/*
|
||||
Write the print session to a file, using the specified processing
|
||||
filter.
|
||||
*/
|
||||
yyout = fopen([fpath fileSystemRepresentation], "wb");
|
||||
if (yyout == NULL)
|
||||
return NO;
|
||||
|
||||
// NSLog(@"Printing to '%@' using filter %d", fpath, filter); //!!
|
||||
fflush(yyin = mPrinter.session);
|
||||
rewind(yyin);
|
||||
|
||||
switch (filter)
|
||||
{
|
||||
default:
|
||||
case kA2PFVerbatim:
|
||||
A2WriteEntireFile(fileno(yyout), fileno(yyin));
|
||||
break;
|
||||
|
||||
case kA2PFEpsonToPS:
|
||||
A2AppendResourceFile(fileno(yyout), @"Ibsen.pfa");
|
||||
A2AppendResourceFile(fileno(yyout), @"IbsenUtils.ps");
|
||||
yylex(filter);
|
||||
break;
|
||||
|
||||
case kA2PFPlain:
|
||||
yylex(filter);
|
||||
break;
|
||||
}
|
||||
|
||||
fclose(yyout);
|
||||
fseek(yyin, 0, SEEK_END);
|
||||
yyin = yyout = NULL;
|
||||
return YES;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
277
Source/LibAppleII/A2Computer/ROM.m
Normal file
277
Source/LibAppleII/A2Computer/ROM.m
Normal file
@ -0,0 +1,277 @@
|
||||
/* class A2Computer (category ROM)
|
||||
|
||||
Methods for dealing with Apple II ROM, and model-specific features.
|
||||
*/
|
||||
#import "LibAppleII-Priv.h"
|
||||
|
||||
@implementation A2Computer (ROM)
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static struct // the ROM repository
|
||||
{
|
||||
uint8_t
|
||||
bogus[0x100], // These are init'd with 'myROM.h'.
|
||||
printer[0x100],
|
||||
clock[0x100],
|
||||
Slinky[0x100],
|
||||
|
||||
DiskII[0x100],
|
||||
SSCX[0x700], SSC[0x100],
|
||||
IIeSerialX[0x700], IIeSerial[0x100],
|
||||
SlinkyX[0x800],
|
||||
Mouse[0x100], // MouseX[0x800],
|
||||
// PIC[0x100],
|
||||
// ThunderClock[0x100], ThunderClockX[0x800],
|
||||
|
||||
IIo[0x3000],
|
||||
IIpD0[0x500], IIpD5[0x3000-0x500],
|
||||
IIeC1[0xF00], IIeD0[0x500], IIeD5[0x3000-0x500],
|
||||
IIcMain [0x3F00], IIcAlt [0x3F00],
|
||||
IIcpMain[0x3F00], IIcpAlt[0x3F00];
|
||||
|
||||
} gROM = {
|
||||
#include "myROM.h"
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
+ (void)_InitROM
|
||||
{/*
|
||||
Called once at startup, from '+initialize'.
|
||||
*/
|
||||
#define PREP(ARR) \
|
||||
memcpy(gROM.ARR + sizeof(gROM.ARR) - 256, gROM.bogus, 256)
|
||||
|
||||
PREP(IIo); PREP(IIpD5); PREP(IIeD5);
|
||||
PREP(IIcMain); PREP(IIcAlt);
|
||||
PREP(IIcpMain); PREP(IIcpAlt);
|
||||
|
||||
[A2Computer ScanDirectoryForROM:nil];
|
||||
|
||||
#undef PREP
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
+ (BOOL)ModelHasROM:(unsigned)modelCode
|
||||
{/*
|
||||
Returns whether ROM for the given Apple II model is available in the
|
||||
ROM repository.
|
||||
*/
|
||||
switch (modelCode)
|
||||
{
|
||||
case kA2ModelIIo:
|
||||
return gROM.IIo[0] != 0;
|
||||
|
||||
case kA2ModelIIp:
|
||||
return gROM.IIpD0[0] and gROM.IIpD5[0];
|
||||
|
||||
case kA2ModelIIe:
|
||||
return gROM.IIeC1[0] and gROM.IIeD0[0] and gROM.IIeD5[0];
|
||||
|
||||
case kA2ModelIIc:
|
||||
return gROM.IIcMain[0] and gROM.IIcAlt[0];
|
||||
|
||||
case kA2ModelIIcp:
|
||||
return gROM.IIcpMain[0] and gROM.IIcpAlt[0];
|
||||
}
|
||||
|
||||
return NO; // means model code isn't valid
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)_InstallPeripheralROM:(unsigned)slotNum
|
||||
:(const uint8_t*)slotROM // size 256
|
||||
:(const uint8_t*)expansionROM // size 2048
|
||||
{/*
|
||||
Private utility method for importing a peripheral's ROM content,
|
||||
given its slot number and pointers to the bytes.
|
||||
*/
|
||||
if (slotNum < 1 or slotNum > 7) // safety check
|
||||
return;
|
||||
|
||||
if (slotROM != nil)
|
||||
memcpy(mMemory->ROM[1] + 0x100*slotNum, slotROM, 0x100);
|
||||
|
||||
if (expansionROM != nil)
|
||||
memcpy(mMemory->ROM[1] + 0x800*slotNum, expansionROM, 0x800);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)_PrepareModel
|
||||
{/*
|
||||
Makes model-specific preparations for this Apple II, including ROM
|
||||
content and flag settings.
|
||||
*/
|
||||
enum
|
||||
{
|
||||
kMF_ec = // set of mutable flags common to IIe and IIc
|
||||
kf80COL | kfSINGRES | kfALTCHAR |
|
||||
kfALTZP | kfRAMRD | kfRAMWRT |
|
||||
kf80STOREm | kf80STOREv
|
||||
};
|
||||
uint8_t *ROM0 = mMemory->ROM[0], // internal, or main bank
|
||||
*ROM1 = mMemory->ROM[1]; // external, or alt. bank
|
||||
|
||||
if (mModel < kA2ModelIIe)
|
||||
mTblADC = A2T.tADCo, mTblSBC = A2T.tSBCo;
|
||||
else
|
||||
mTblADC = A2T.tADC , mTblSBC = A2T.tSBC;
|
||||
|
||||
mMutableFlags = 0;
|
||||
memset(mMemory->ROM, 0, sizeof(mMemory->ROM)); // wipe ROM clean
|
||||
|
||||
for (int i = 0; i <= 7; ++i) // for debugging memory mapping!!
|
||||
memset(ROM1 + 0x800*i, i*0x11, 0x800);
|
||||
|
||||
// Install the machine's primary ROM, copied from the repository.
|
||||
|
||||
switch (mModel)
|
||||
{
|
||||
case kA2ModelIIo:
|
||||
memcpy(ROM0 + 0x1000, gROM.IIo, 0x3000);
|
||||
goto PrepIIo_p_e;
|
||||
|
||||
case kA2ModelIIp:
|
||||
memcpy(ROM0 + 0x1000, gROM.IIpD0, 0x3000);
|
||||
goto PrepIIo_p_e;
|
||||
|
||||
case kA2ModelIIe:
|
||||
memcpy(ROM0 + 0x0100, gROM.IIeC1, 0x3F00);
|
||||
mMutableFlags |= kMF_ec | kfCXROM | kfC3ROM;
|
||||
// fall into PrepIIo_p_e
|
||||
|
||||
PrepIIo_p_e:
|
||||
[self _InstallPeripheralROM:1 :gROM.SSC :gROM.SSCX];
|
||||
[self _InstallPeripheralROM:2 :gROM.clock :nil];
|
||||
[self _InstallPeripheralROM:4 :gROM.Slinky :gROM.SlinkyX];
|
||||
[self _InstallPeripheralROM:6 :gROM.DiskII :nil];
|
||||
memcpy(mMemory->altSlotROM, ROM1, 0x800);
|
||||
memcpy(mMemory->altSlotROM+0x300, ROM0+0x300, 0x100);
|
||||
memcpy(mPrinter.reg,
|
||||
"\x68\xEE\x7B\xFF\x68\xEE\x7B\xFF"
|
||||
"\0\x10\0\0\xFF\xFF\xFF\xFF", 16);
|
||||
break;
|
||||
|
||||
|
||||
case kA2ModelIIcp:
|
||||
memcpy(ROM0 + 0x0100, gROM.IIcpMain, 0x3F00);
|
||||
memcpy(ROM1 + 0x0100, gROM.IIcpAlt , 0x3F00);
|
||||
goto PrepIIc;
|
||||
|
||||
case kA2ModelIIc:
|
||||
// Check for older, single-bank IIc ROM!!
|
||||
memcpy(ROM0 + 0x0100, gROM.IIcMain, 0x3F00);
|
||||
memcpy(ROM1 + 0x0100, gROM.IIcAlt , 0x3F00);
|
||||
// fall into PrepIIc;
|
||||
|
||||
PrepIIc:
|
||||
mMutableFlags |= kMF_ec;
|
||||
memcpy(mPrinter.reg,
|
||||
"\0\x50\0\0\0\x50\0\0\0\x50\0\0\0\x50\0\0", 16);
|
||||
// memcpy(mModem.reg,
|
||||
// "\0\x10\0\0\0\x10\0\0\0\x10\0\0\0\x10\0\0", 16);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
+ (BOOL)ScanFileForROM:(NSString*)filePath
|
||||
{/*
|
||||
Scans the given file, looking for ROM segments that we recognize. A
|
||||
segment is recognized if the checksum of its first 256 bytes matches
|
||||
one that we've precomputed. Segments are then read into the ROM
|
||||
repository, defined above.
|
||||
*/
|
||||
#define CASE(N, ARR) case 0x##N: \
|
||||
dest = gROM.ARR; len = sizeof(gROM.ARR); break;
|
||||
|
||||
enum { kDebug = NO,
|
||||
chunkSize = 256 };
|
||||
uint8_t chunk[chunkSize];
|
||||
NSInputStream* sin;
|
||||
|
||||
if (nil == (sin = [[NSInputStream alloc] initWithFileAtPath:filePath]))
|
||||
return NO;
|
||||
[sin open];
|
||||
|
||||
if (kDebug)
|
||||
NSLog(@"Scanning ROM file '%@'", [filePath lastPathComponent]);
|
||||
|
||||
while ([sin read:chunk maxLength:chunkSize] == chunkSize)
|
||||
{
|
||||
uint32_t crc = adler32(~0UL, chunk, chunkSize);
|
||||
uint8_t* dest;
|
||||
long len;
|
||||
|
||||
if (kDebug)
|
||||
NSLog(@"%05lX: crc=%08X",
|
||||
[[sin propertyForKey:NSStreamFileCurrentOffsetKey] longValue]
|
||||
- chunkSize, crc);
|
||||
|
||||
switch (crc)
|
||||
{
|
||||
CASE(5FCB5D2A, IIo)
|
||||
CASE(B2ADA4E6, IIpD0) CASE(F3048537, IIpD5)
|
||||
CASE(EDA770F0, IIeC1) CASE(085488C1, IIeD5)
|
||||
|
||||
CASE(A3BB7671, IIcMain) CASE(A9A56CEC, IIcAlt)
|
||||
CASE(A40E7672, IIcpMain) CASE(A99F6CE9, IIcpAlt)
|
||||
|
||||
CASE(9C377B54, DiskII)
|
||||
// CASE(39797894, Mouse)
|
||||
// CASE(67D46AFF, SSC) CASE(B2EB6D44, SSCX)
|
||||
// CASE(C37D631F, IIeSerial) CASE(CDFB877A, IIeSerialX)
|
||||
// CASE(FCE2762B, Slinky) CASE(807A73D1, SlinkyX)
|
||||
|
||||
default: continue; // chunk not recognized; continue reading
|
||||
}
|
||||
memcpy(dest, chunk, chunkSize);
|
||||
[sin read:dest+chunkSize maxLength:len-chunkSize];
|
||||
|
||||
if (dest == gROM.IIpD0)
|
||||
memcpy(gROM.IIeD0, dest, len);
|
||||
}
|
||||
|
||||
[sin close]; // need??
|
||||
[sin release];
|
||||
return YES;
|
||||
|
||||
#undef CASE
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
+ (void)ScanDirectoryForROM:(NSString*)dirPath
|
||||
{/*
|
||||
Scans every file in the given directory for recognized segments of
|
||||
ROM. If nil is passed, the default directory is "ROMs", at the same
|
||||
level as the application bundle.
|
||||
*/
|
||||
if (dirPath == nil)
|
||||
dirPath = [[[NSBundle mainBundle] bundlePath]
|
||||
stringByAppendingPathComponent:@"../ROMs"];
|
||||
|
||||
NSEnumerator *e;
|
||||
NSString *fname, *fpath;
|
||||
|
||||
e = [[[NSFileManager defaultManager]
|
||||
directoryContentsAtPath:dirPath] objectEnumerator];
|
||||
if (e == nil)
|
||||
return;
|
||||
|
||||
while (nil != (fname = [e nextObject]))
|
||||
{
|
||||
if ([fname characterAtIndex:0] != '.')
|
||||
{
|
||||
fpath = [dirPath stringByAppendingPathComponent:fname];
|
||||
[A2Computer ScanFileForROM:fpath];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
333
Source/LibAppleII/A2Computer/UserInterface.m
Normal file
333
Source/LibAppleII/A2Computer/UserInterface.m
Normal file
@ -0,0 +1,333 @@
|
||||
/* class A2Computer (category UserInterface)
|
||||
|
||||
Methods having to do with user interaction: keypresses, the
|
||||
joystick/paddle values and button states, states of the indicator
|
||||
lights, etc.
|
||||
*/
|
||||
#import "LibAppleII-Priv.h"
|
||||
#import "A2DiskDrive.h"
|
||||
|
||||
@implementation A2Computer (UserInterface)
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static NSRect gMouseRange;
|
||||
|
||||
static NSString* gNameOfModel[] =
|
||||
{
|
||||
@"??", // code 0 is invalid
|
||||
|
||||
@"][", @"][+", @"//e", @"//c", @"//c+",
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (IBAction)SignalReset:(id)sender
|
||||
{/*
|
||||
Informs this Apple II that the user invoked RESET. The Apple II will
|
||||
respond to it at a later time.
|
||||
*/
|
||||
mHalts |= kfHaltReset;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (IBAction)SignalReboot:(id)sender
|
||||
{/*
|
||||
Informs this Apple II that the user invoked a reboot (a "cold reset").
|
||||
The Apple II will respond to it at a later time.
|
||||
*/
|
||||
mMemory->RAM[0][0][1012] = mMemory->RAM[0][0][1011] = 0;
|
||||
// sabotages the "power-up" byte
|
||||
|
||||
mHalts |= kfHaltReset;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (BOOL)InputChar:(unichar)ch
|
||||
{/*
|
||||
Informs this Apple II of a character typed on the keyboard. Returns
|
||||
whether the character was successfully queued. The character code
|
||||
must be plain ASCII (range 0-127), as that's all the Apple II ever
|
||||
supported.
|
||||
|
||||
Also, this method turns on the "power" if it isn't already on.
|
||||
*/
|
||||
if ( (uint8_t)(mKeyQ.tail - mKeyQ.head) > 250 or ch > 127 )
|
||||
return NO;
|
||||
|
||||
if (mModel < kA2ModelIIe)
|
||||
ch = toupper(ch);
|
||||
|
||||
mKeyQ.buf[mKeyQ.tail++] = ch; // enqueue the character
|
||||
mKeyQ.hitRecently = YES;
|
||||
mHalts &= ~kfHaltNoPower;
|
||||
return YES;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)InputChars:(NSString*)str
|
||||
{/*
|
||||
Puts the given string of characters into the Apple II's keyboard
|
||||
queue, as if they had all been typed -- really really quickly.
|
||||
*/
|
||||
int len = [str length];
|
||||
|
||||
if (len > 250)
|
||||
len = 250;
|
||||
mKeyQ.head = mKeyQ.tail = 0;
|
||||
[str getCString:(char*)(mKeyQ.buf) maxLength:len];
|
||||
mKeyQ.tail = len;
|
||||
mKeyQ.hitRecently = YES;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
+ (void)InputPaddlesByKeypad:(char)ch
|
||||
{/*
|
||||
Sets the paddle values (and joystick position) according to a digit
|
||||
key on the numeric keypad.
|
||||
*/
|
||||
div_t d = div((ch - 1) & 15, 3);
|
||||
|
||||
A2G.paddle[0] = (kA2PaddleRange/2) * d.rem;
|
||||
A2G.paddle[1] = (kA2PaddleRange/2) * (2 - d.quot);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
+ (void)InputPaddlesByMouse
|
||||
{/*
|
||||
Sets the paddle values (also joystick position) by the current
|
||||
coordinates of the host machine's mouse. Only paddles #0 and #1 are
|
||||
affected.
|
||||
*/
|
||||
NSPoint mloc = [NSEvent mouseLocation];
|
||||
int p0, p1;
|
||||
|
||||
p0 = (mloc.x - gMouseRange.origin.x) * gMouseRange.size.width;
|
||||
p1 = kA2PaddleRange -
|
||||
(mloc.y - gMouseRange.origin.y) * gMouseRange.size.height;
|
||||
|
||||
if (p0 < 0)
|
||||
p0 = 0;
|
||||
A2G.paddle[0] = p0;
|
||||
|
||||
if (p1 < 0)
|
||||
p1 = 0;
|
||||
A2G.paddle[1] = p1;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
+ (void)SetMouseRangeTo:(NSRect)r
|
||||
{/*
|
||||
Informs the library of the extent rectangle over which the mouse's
|
||||
coordinates may roam. Affects the future behavior of
|
||||
+InputPaddlesByMouse.
|
||||
*/
|
||||
gMouseRange.origin = r.origin;
|
||||
gMouseRange.size.width = kA2PaddleRange / r.size.width;
|
||||
gMouseRange.size.height = kA2PaddleRange / r.size.height;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)_RespondToReset
|
||||
{
|
||||
#if 0
|
||||
strcpy(((char*)mMemory->RAM[0][0]) + 0x309, //!!
|
||||
"\xA9\xA\x20\xA8\xFC\xAD\x30\xC0\x4C\x09\3");
|
||||
#endif
|
||||
|
||||
mFlags = kfTEXT | kfLCWRThi | kfLCBANK2 | kfC3ROM;
|
||||
if (mModel < kA2ModelIIc)
|
||||
mFlags |= 3UL << ksHotSlot;
|
||||
memset(mVideoFlags, mFlags, sizeof(mVideoFlags));
|
||||
|
||||
mKeyQ.tail = mKeyQ.head = 0;
|
||||
mPC = mMemory->ROM[0][0x3FFD] << 8 // load PC from $FFFC-D in ROM
|
||||
| mMemory->ROM[0][0x3FFC];
|
||||
|
||||
// mPrinter.reg[0x8] = ??;
|
||||
mPrinter.reg[0xA] = 0; // command reg
|
||||
mPrinter.reg[0xB] = 0x3E; // control reg
|
||||
mPrinter.lights = 0;
|
||||
|
||||
mIWM[0].flags = mIWM[1].flags = 0;
|
||||
mIWM[0].lights = mIWM[1].lights = 0;
|
||||
mIWM[0].modeReg = mIWM[1].modeReg = 0; // Does mode-reg reset??
|
||||
|
||||
// mSlinky.pos = 0; // Does this reset??
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (unsigned)Lights
|
||||
{/*
|
||||
Returns the status of the Apple II's indicator lights as a bit vector.
|
||||
Should be called about 3 to 4 times per second from the client
|
||||
application.
|
||||
*/
|
||||
if (mHalts & kfHaltReset) // then RESET was raised earlier
|
||||
{
|
||||
[self _RespondToReset];
|
||||
mHalts &= ~kfHaltReset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned lights = 0;
|
||||
|
||||
for (int i = 2; --i >= 0;)
|
||||
{
|
||||
A2IWM* iwm = mIWM + i;
|
||||
unsigned lt = iwm->lights;
|
||||
|
||||
if (lt >= 16)
|
||||
iwm->lights -= 16, lights |= (lt&3) << (2*i);
|
||||
}
|
||||
|
||||
if (mPrinter.lights >= 16)
|
||||
mPrinter.lights -= 16, lights |= kfA2LightPrinter;
|
||||
|
||||
if (not mKeyQ.hitRecently and mKeyQ.tail != mKeyQ.head)
|
||||
mKeyQ.head++;
|
||||
mKeyQ.hitRecently = NO;
|
||||
|
||||
return lights;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static inline uint8_t ASCIIfy(int charset, uint8_t ch)
|
||||
{/*
|
||||
Returns the ASCII equivalent of an Apple II character screen code
|
||||
(0-255), or a space character if there is no such thing.
|
||||
*/
|
||||
static uint8_t flip[3][8] =
|
||||
{
|
||||
0x40,0x00,0x00,0x40, 0xC0,0x80,0x80,0x80, // IIe std charset
|
||||
0x40,0x00,0x40,0x00, 0xC0,0x80,0x80,0x80, // IIe alt (MouseText)
|
||||
0x40,0x00,0x00,0x40, 0xC0,0x80,0x80,0xA0, // IIo and IIp
|
||||
};
|
||||
|
||||
ch ^= flip[charset][ch >> 5];
|
||||
return (ch < 32 or ch > 126)? 32 : ch;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (NSString*)TextScreenAsString:(BOOL)newLines
|
||||
{/*
|
||||
Returns the content of the Apple II's text screen as a giant string of
|
||||
characters. Optionally puts newline characters at the end of each
|
||||
screen line. Returns nil if no text is visible.
|
||||
*/
|
||||
unsigned f = mFlags;
|
||||
|
||||
if ((f & (kfTEXT | kfMIXED)) == 0) // then in full-graphics mode
|
||||
return nil;
|
||||
|
||||
|
||||
char cstr[24*81 + 1], *pout = cstr;
|
||||
uint8_t* dispPage = mMemory->RAM[0][0] + 0x400;
|
||||
int charset; // 0-2
|
||||
|
||||
charset = (mModel < kA2ModelIIe)? 2 : (f >> ksALTCHAR & 1);
|
||||
if ((f & (kf80STOREv | kfPAGE2v)) == kfPAGE2v)
|
||||
dispPage += 0x400;
|
||||
|
||||
for (int v = (f & kfTEXT)? 0 : 20; v < 24; ++v)
|
||||
{
|
||||
uint8_t *pin = dispPage + 128*(v&7) + 5*(v&~7);
|
||||
|
||||
for (int h = 0; h < 40; ++h)
|
||||
{
|
||||
if (f & kf80COL)
|
||||
*pout++ = ASCIIfy(charset, pin[0x2000+h]);
|
||||
*pout++ = ASCIIfy(charset, pin[h]);
|
||||
}
|
||||
if (newLines)
|
||||
*pout++ = '\n';
|
||||
}
|
||||
|
||||
return [NSString stringWithCString:cstr length:(pout-cstr)];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
+ (void)_UpdateClock:(NSTimer*)timer
|
||||
{
|
||||
static struct
|
||||
{
|
||||
char hi[100], lo[100];
|
||||
}
|
||||
digit =
|
||||
{
|
||||
"00000000001111111111222222222233333333334444444444"
|
||||
"55555555556666666666777777777788888888889999999999",
|
||||
"01234567890123456789012345678901234567890123456789"
|
||||
"01234567890123456789012345678901234567890123456789"
|
||||
};
|
||||
|
||||
static time_t tPrev; // = 0
|
||||
time_t t;
|
||||
struct tm tm;
|
||||
|
||||
if (tPrev == time(&t))
|
||||
return;
|
||||
|
||||
// NSLog(@"Clock time being updated.");
|
||||
|
||||
tPrev = t;
|
||||
A2G.timeInGMT? gmtime_r(&t, &tm) : localtime_r(&t, &tm);
|
||||
tm.tm_year %= 100;
|
||||
tm.tm_mon += 1;
|
||||
|
||||
uint8_t str[32] =
|
||||
{
|
||||
tm.tm_mon << 5 | tm.tm_mday,
|
||||
tm.tm_year << 1 | tm.tm_mon >> 3,
|
||||
tm.tm_min, tm.tm_hour,
|
||||
|
||||
digit.hi[tm.tm_mon ], digit.lo[tm.tm_mon ], ',',
|
||||
'0', digit.lo[tm.tm_wday], ',',
|
||||
digit.hi[tm.tm_mday], digit.lo[tm.tm_mday], ',',
|
||||
digit.hi[tm.tm_hour], digit.lo[tm.tm_hour], ',',
|
||||
digit.hi[tm.tm_min ], digit.lo[tm.tm_min ], ',',
|
||||
digit.hi[tm.tm_sec ], digit.lo[tm.tm_sec ], 13,
|
||||
};
|
||||
|
||||
for (int i = 32/4; --i >= 0;)
|
||||
((uint32_t*)(A2T.curTime))[i] = ((uint32_t*)str)[i];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (id<A2PrDiskDrive>)DiskDrive:(unsigned)index
|
||||
{/*
|
||||
Returns the disk drive object identified by the given index (0-3).
|
||||
*/
|
||||
return (index > 3)? nil :
|
||||
mIWM[index >> 1].drive[index & 1];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (NSString*)ModelName
|
||||
{ return gNameOfModel[mModel]; }
|
||||
// Returns a short name for this Apple II's model.
|
||||
|
||||
- (unsigned)ModelCode
|
||||
{ return mModel; }
|
||||
// Returns the numeric code for this Apple II's model.
|
||||
|
||||
- (BOOL)acceptsFirstResponder
|
||||
{ return YES; }
|
||||
|
||||
+ (BOOL)ShouldShowDiskFilename:(NSString*)path
|
||||
{ return [A2DiskDrive ShouldShowFilename:path]; }
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
410
Source/LibAppleII/A2Computer/Video.m
Normal file
410
Source/LibAppleII/A2Computer/Video.m
Normal file
@ -0,0 +1,410 @@
|
||||
/* class A2Computer (category Video)
|
||||
|
||||
Routines and tables for rendering frames of Apple II video.
|
||||
*/
|
||||
#import "LibAppleII-Priv.h"
|
||||
|
||||
@implementation A2Computer (Video)
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static unsigned DoubleBits(uint8_t b)
|
||||
{/*
|
||||
Returns a 16-bit vector formed by replicating the bits of an 8-bit
|
||||
vector. (Each 0 becomes 00, and each 1 becomes 11.)
|
||||
*/
|
||||
static unsigned dbl[16] =
|
||||
{
|
||||
0x00,0x03,0x0C,0x0F, 0x30,0x33,0x3C,0x3F,
|
||||
0xC0,0xC3,0xCC,0xCF, 0xF0,0xF3,0xFC,0xFF,
|
||||
};
|
||||
|
||||
return dbl[b>>4]<<8 | dbl[b&15];
|
||||
}
|
||||
|
||||
|
||||
static unsigned SpreadBits(uint8_t b)
|
||||
{ return DoubleBits(b) & 0x5555; }
|
||||
// Expands an 8-bit vector to 16, interleaving it with zeros.
|
||||
|
||||
|
||||
static unsigned ShiftColor(unsigned c, int s)
|
||||
{ return c & 0xF0 | 0x0F & (((c&0x0F)*0x11)>>(s&3)); }
|
||||
// { return ((c&0xF)*0x11) >> (s&3) & 0xF; }
|
||||
|
||||
|
||||
static inline unsigned RandomByte(long* r)
|
||||
{ *r = *r * 157 % 32363; return 0xFF & (*r>>8 ^ *r); }
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static void InitHPixelTables(void)
|
||||
{
|
||||
uint8_t colorOfBits[1 << 6] =
|
||||
{
|
||||
// 0 1 2 3 4 5 6 7
|
||||
/* 0 */ 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x3, 0x3,
|
||||
/* 1 */ 0x4, 0xC, 0x5, 0xD, 0x6, 0xE, 0x7, 0xF,
|
||||
/* 2 */ 0x0, 0x8, 0x9, 0x9, 0xA, 0xA, 0xB, 0xB,
|
||||
/* 3 */ 0xC, 0xC, 0xD, 0xD, 0xE, 0xE, 0xF, 0xF,
|
||||
/* 4 */ 0x0, 0x0, 0x1, 0x1, 0x3, 0x3, 0x3, 0x3,
|
||||
/* 5 */ 0x4, 0xC, 0x5, 0xD, 0x7, 0xF, 0x7, 0xF, // 55??
|
||||
/* 6 */ 0x0, 0x0, 0x9, 0x9, 0xB, 0xB, 0xB, 0xB,
|
||||
/* 7 */ 0xC, 0xC, 0xD, 0xD, 0xF, 0xF, 0xF, 0xF,
|
||||
};
|
||||
|
||||
for (int i = 64; --i >= 0;)
|
||||
colorOfBits[i] |= (i << 2 & 16); // monochrome bit
|
||||
|
||||
for (int i = LENGTH(A2T.vidPix.Hd); --i >= 0;)
|
||||
for (int j = 4; --j >= 0;)
|
||||
((uint8_t*) (A2T.vidPix.Hd + i))[j] =
|
||||
ShiftColor(colorOfBits[i>>j & 0x3F], 2-j);
|
||||
|
||||
for (int i = LENGTH(A2T.vidPix.Hs); --i >= 0;)
|
||||
{
|
||||
unsigned mono = 0;
|
||||
// the pattern of monochrome pixels _i_ represents
|
||||
|
||||
for (int j = 0; j <= 6; j += 2)
|
||||
mono |= ("\0\3\0\2\4\7\4\6\0\7\0\6\0\7\0\6"[i>>j & 15]) << j;
|
||||
mono &= 0x1FF;
|
||||
|
||||
for (int j = 4; --j >= 0;)
|
||||
((uint8_t*) (A2T.vidPix.Hs + i))[j] =
|
||||
ShiftColor(((uint8_t*)(A2T.vidPix.Hd + mono))[j], 1);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static void InitPixelTables(void)
|
||||
{/*
|
||||
Initializes the 'A2T.vpix' tables, which map video bit patterns to
|
||||
quartets of pixel values.
|
||||
|
||||
The eight-bit pixel values used:
|
||||
0x00-0F HGR & DHGR colors, monochrome off
|
||||
10-1F HGR & DHGR colors, monochrome on
|
||||
20-2F GR & DGR colors (shades from 0 to 5 when in monochrome)
|
||||
30-33 text: steady black, white; flashing black, white
|
||||
*/
|
||||
InitHPixelTables();
|
||||
|
||||
for (int i = 16; --i >= 0;)
|
||||
A2T.vidPix.G[i] = 0x01010101 * (0x20 | i);
|
||||
|
||||
for (int i = LENGTH(A2T.vidPix.Ts); --i >= 0;)
|
||||
{
|
||||
uint8_t *pix = (uint8_t*)(A2T.vidPix.Ts + i);
|
||||
|
||||
pix[0] = pix[1] = 0x30 | (i & 3);
|
||||
pix[2] = pix[3] = 0x30 | (i>>2 & 3);
|
||||
}
|
||||
|
||||
for (int i = LENGTH(A2T.vidPix.Td); --i >= 0;)
|
||||
{
|
||||
uint8_t *pix = (uint8_t*)(A2T.vidPix.Td + i);
|
||||
|
||||
pix[0] = 0x30 | (i & 3);
|
||||
pix[1] = 0x30 | (i>>2 & 3);
|
||||
pix[2] = 0x30 | (i>>4 & 3);
|
||||
pix[3] = 0x30 | (i>>6 & 3);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static void InitBitPatterns(void)
|
||||
{
|
||||
#include "glyphs.xbm"
|
||||
// defines: static char glyphs_xbm_bits[10*9*16]
|
||||
|
||||
char fix = ~glyphs_xbm_bits[0x20 * 9]; // bits from space character
|
||||
|
||||
for (int ch = 128; --ch >= 0;)
|
||||
{
|
||||
char* xbm = glyphs_xbm_bits + (ch&0x70)*9 + (ch&15);
|
||||
|
||||
for (int row = 0; row < 8; ++row, xbm += 16)
|
||||
{
|
||||
A2T.vidBits.T[1][row][128+ch] = 0x1555 ^ (
|
||||
A2T.vidBits.T[1][row][ch] =
|
||||
SpreadBits((*xbm ^ fix) & 0x7F) );
|
||||
}
|
||||
}
|
||||
|
||||
for (int row = 8; --row >= 0;)
|
||||
{
|
||||
uint16_t *p0 = A2T.vidBits.T[0][row],
|
||||
*p1 = A2T.vidBits.T[1][row],
|
||||
*p2 = A2T.vidBits.T[2][row];
|
||||
|
||||
memcpy(p1+0x40, p1+0xC0, 0x20*2); // Mouse Text
|
||||
memcpy(p1+0xC0, p1+0x80, 0x20*2); // capitals
|
||||
|
||||
memcpy(p0, p1, 0x100*2);
|
||||
memcpy(p0+0x40, p0, 0x40*2);
|
||||
for (int i = 0x40; i <= 0x7F; ++i)
|
||||
p0[i] |= 0x2AAA;
|
||||
|
||||
memcpy(p2, p0, 0x100*2);
|
||||
memcpy(p2+0xE0, p0+0xC0, 0x20*2);
|
||||
}
|
||||
|
||||
for (long i = sizeof(A2T.vidBits.T)/2; --i >= 0;)
|
||||
(A2T.vidBits.T[0][0])[i] <<= 2;
|
||||
|
||||
for (int i = 0x80; --i >= 0;)
|
||||
{
|
||||
uint32_t sb = SpreadBits(i);
|
||||
|
||||
A2T.vidBits.Hs[ i] = sb << 6;
|
||||
A2T.vidBits.Hs[0x80+i] = (sb | 0x2AAA) << 6;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
+ (void)_InitVideo
|
||||
{
|
||||
InitBitPatterns();
|
||||
InitPixelTables();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)RenderScreen:(void*)pixBase:(int32_t)rowBytes
|
||||
{/*
|
||||
Renders a frame of Apple II video into the given 8-bit deep image.
|
||||
|
||||
Argument 'pixBase' points to the upper-left or lower-left pixel;
|
||||
'rowBytes' is the stride in memory from one row to the next -- the
|
||||
delta between pixels that are vertically adjacent.
|
||||
*/
|
||||
PixelGroup* pout = (PixelGroup*) pixBase;
|
||||
|
||||
rowBytes /= sizeof(*pout);
|
||||
|
||||
if (mHalts & kfHaltNoPower) // then power is off; render a screen of snow
|
||||
{
|
||||
for (int v = 192; --v >= 0; pout += rowBytes)
|
||||
{
|
||||
for (int h = 141; (h -= 4) >= 0;)
|
||||
{
|
||||
enum { kWhite4 = 0x2F * 0x01010101 };
|
||||
unsigned r = A2Random16();
|
||||
|
||||
r &= r >> 4 & r >> 8;
|
||||
(pout+h)[0] = kWhite4 & -(r&1); r >>= 1;
|
||||
(pout+h)[1] = kWhite4 & -(r&1); r >>= 1;
|
||||
(pout+h)[2] = kWhite4 & -(r&1); r >>= 1;
|
||||
(pout+h)[3] = kWhite4 & -(r&1);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
// Otherwise the power is on, so render a real screen of
|
||||
// Apple II video.
|
||||
|
||||
#define SETUP_FOR(TYPE) \
|
||||
enum { kMask = (LENGTH(A2T.vidPix.TYPE) - 1) << 2, \
|
||||
kShift = kShift##TYPE }; \
|
||||
char* tpix = (char*)(A2T.vidPix.TYPE);
|
||||
|
||||
#define BAM_(I) pout[I] = *(PixelGroup*)(tpix + (bits & kMask))
|
||||
|
||||
#define BAM(I) BAM_(I); bits >>= kShift
|
||||
|
||||
#define HLOOP \
|
||||
for (v -= 21*192; (v+=192) < 0; pout += 7, pin += 2)
|
||||
|
||||
#define GCASE(N) \
|
||||
case kfUpRow + kfMIXED + (N): \
|
||||
case kfUpRow + (N): \
|
||||
case kfLoRow + (N)
|
||||
|
||||
#define TCASE(N) \
|
||||
case kfLoRow + kfMIXED + (N): \
|
||||
case kfUpRow + kfTEXT + (N): \
|
||||
case kfLoRow + kfTEXT + (N): \
|
||||
case kfUpRow + kfTEXT + kfMIXED + (N): \
|
||||
case kfLoRow + kfTEXT + kfMIXED + (N)
|
||||
|
||||
enum
|
||||
{
|
||||
kAux = 0x2000,
|
||||
kfUpRow = 1 << 5,
|
||||
kfLoRow = 0,
|
||||
|
||||
kShiftTs = 4, kShiftHs = 4,
|
||||
kShiftTd = 8, kShiftHd = 4,
|
||||
};
|
||||
|
||||
rowBytes -= 20 * 7;
|
||||
|
||||
for (int v = -1; ++v < 192; pout += rowBytes)
|
||||
{
|
||||
uint32_t bits = mFlags; //!! mVideoFlags[v];
|
||||
uint8_t* pin = (mMemory->RAM[0][0]) +
|
||||
((v<<4 & 0x380) + 40*(v>>6));
|
||||
int dispPage =
|
||||
0x400 << (2 >> (bits>>ksPAGE2v & 3) & 1);
|
||||
// = either 0x400 or 0x800
|
||||
|
||||
switch ( bits & 0x1F | ((v>>5)-5) & 0x20 )
|
||||
{
|
||||
//------------------------------------- 40-column text
|
||||
|
||||
default:
|
||||
TCASE( 0 ):
|
||||
TCASE( + kfSINGRES):
|
||||
TCASE( + kfHIRESv ):
|
||||
TCASE( + kfHIRESv + kfSINGRES):
|
||||
{
|
||||
SETUP_FOR(Ts)
|
||||
uint16_t* tbits;
|
||||
|
||||
tbits = A2T.vidBits.T[bits>>ksALTCHAR & 1][v&7];
|
||||
pin += dispPage;
|
||||
*pout++ = 0;
|
||||
|
||||
HLOOP
|
||||
{
|
||||
bits = tbits[pin[0]];
|
||||
BAM(0); BAM(1); BAM(2);
|
||||
bits |= tbits[pin[1]] << 2;
|
||||
BAM(3); BAM(4); BAM(5); BAM_(6);
|
||||
}
|
||||
*pout-- = 0;
|
||||
} break;
|
||||
|
||||
//------------------------------------- 80-column text
|
||||
|
||||
TCASE(+ kf80COL ):
|
||||
TCASE(+ kf80COL + kfSINGRES):
|
||||
TCASE(+ kf80COL + kfHIRESv ):
|
||||
TCASE(+ kf80COL + kfHIRESv + kfSINGRES):
|
||||
{
|
||||
SETUP_FOR(Td)
|
||||
uint16_t* tbits;
|
||||
|
||||
tbits = A2T.vidBits.T[bits>>ksALTCHAR & 1][v&7];
|
||||
pin += dispPage;
|
||||
*pout++ = 0;
|
||||
|
||||
HLOOP
|
||||
{
|
||||
bits = tbits[pin[kAux+0]];
|
||||
BAM(0);
|
||||
bits |= tbits[pin[0]] << 6;
|
||||
BAM(1); BAM(2);
|
||||
bits |= tbits[pin[kAux+1]] << 4;
|
||||
BAM(3); BAM(4);
|
||||
bits |= tbits[pin[1]] << 2;
|
||||
BAM(5); BAM_(6);
|
||||
}
|
||||
*pout-- = 0;
|
||||
|
||||
} break;
|
||||
|
||||
//------------------------------------- 40-column GR
|
||||
|
||||
GCASE( 0 ):
|
||||
GCASE( + kfSINGRES):
|
||||
GCASE(+ kf80COL + kfSINGRES):
|
||||
{
|
||||
pin += dispPage;
|
||||
*pout++ = 0;
|
||||
|
||||
HLOOP
|
||||
{
|
||||
pout[0] = pout[1] = pout[2] = pout[3] =
|
||||
A2T.vidPix.G[pin[0] >> (v&4) & 15];
|
||||
|
||||
((uint16_t*)pout)[7] = pout[4] = pout[5] = pout[6] =
|
||||
A2T.vidPix.G[pin[1] >> (v&4) & 15];
|
||||
}
|
||||
*pout-- = 0;
|
||||
|
||||
} break;
|
||||
|
||||
//------------------------------------- 80-column GR
|
||||
|
||||
GCASE(+ kf80COL ):
|
||||
{
|
||||
pin += dispPage;
|
||||
*pout++ = 0;
|
||||
|
||||
HLOOP
|
||||
{
|
||||
pout[0] = pout[1] =
|
||||
A2T.vidPix.G[pin[kAux+0] >> (v&4) & 15];
|
||||
|
||||
((uint8_t*)pout)[7] = pout[2] = pout[3] =
|
||||
A2T.vidPix.G[pin[0] >> (v&4) & 15];
|
||||
|
||||
((uint16_t*)pout)[7] = pout[4] = pout[5] =
|
||||
A2T.vidPix.G[pin[kAux+1] >> (v&4) & 15];
|
||||
|
||||
((uint8_t*)pout)[21] = ((uint16_t*)pout)[11] = pout[6] =
|
||||
A2T.vidPix.G[pin[1] >> (v&4) & 15];
|
||||
}
|
||||
*pout-- = 0;
|
||||
|
||||
} break;
|
||||
|
||||
//------------------------------------- HGR
|
||||
|
||||
GCASE( + kfHIRESv ):
|
||||
GCASE( + kfHIRESv + kfSINGRES):
|
||||
GCASE(+ kf80COL + kfHIRESv + kfSINGRES):
|
||||
{
|
||||
SETUP_FOR(Hs)
|
||||
|
||||
pin += dispPage<<4 | (v&7)<<10;
|
||||
bits = 0;
|
||||
|
||||
HLOOP
|
||||
{
|
||||
bits |= A2T.vidBits.Hs[pin[0]];
|
||||
BAM(0); BAM(1); BAM(2);
|
||||
bits |= A2T.vidBits.Hs[pin[1]] << 2;
|
||||
BAM(3); BAM(4); BAM(5); BAM(6);
|
||||
}
|
||||
BAM(0); BAM_(1);
|
||||
|
||||
} break;
|
||||
|
||||
//------------------------------------- DHGR
|
||||
|
||||
GCASE(+ kf80COL + kfHIRESv ):
|
||||
{
|
||||
SETUP_FOR(Hd)
|
||||
|
||||
pin += dispPage<<4 | (v&7)<<10;
|
||||
bits = 0;
|
||||
|
||||
HLOOP
|
||||
{
|
||||
bits |= (pin[kAux+0] & 0x7F) << 6;
|
||||
BAM(0);
|
||||
bits |= (pin[0] & 0x7F) << 9;
|
||||
BAM(1); BAM(2);
|
||||
bits |= (pin[kAux+1] & 0x7F) << 8;
|
||||
BAM(3); BAM(4);
|
||||
bits |= (pin[1] & 0x7F) << 7;
|
||||
BAM(5); BAM(6);
|
||||
}
|
||||
BAM(0); BAM_(1);
|
||||
|
||||
} break;
|
||||
|
||||
} // end of switch on video flags
|
||||
|
||||
} // end of for (v...)
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
124
Source/LibAppleII/A2Computer/glyphs.xbm
Normal file
124
Source/LibAppleII/A2Computer/glyphs.xbm
Normal file
@ -0,0 +1,124 @@
|
||||
#define glyphs_xbm_width 128
|
||||
#define glyphs_xbm_height 90
|
||||
static char glyphs_xbm_bits[] = {
|
||||
0x1C, 0x08, 0x1E, 0x1C, 0x1E, 0x3E, 0x3E, 0x3C, 0x22, 0x1C, 0x20, 0x22,
|
||||
0x02, 0x22, 0x22, 0x1C, 0x22, 0x14, 0x22, 0x22, 0x22, 0x02, 0x02, 0x02,
|
||||
0x22, 0x08, 0x20, 0x12, 0x02, 0x36, 0x22, 0x22, 0x2A, 0x22, 0x22, 0x02,
|
||||
0x22, 0x02, 0x02, 0x02, 0x22, 0x08, 0x20, 0x0A, 0x02, 0x2A, 0x26, 0x22,
|
||||
0x3A, 0x22, 0x1E, 0x02, 0x22, 0x1E, 0x1E, 0x02, 0x3E, 0x08, 0x20, 0x06,
|
||||
0x02, 0x2A, 0x2A, 0x22, 0x1A, 0x3E, 0x22, 0x02, 0x22, 0x02, 0x02, 0x32,
|
||||
0x22, 0x08, 0x20, 0x0A, 0x02, 0x22, 0x32, 0x22, 0x02, 0x22, 0x22, 0x22,
|
||||
0x22, 0x02, 0x02, 0x22, 0x22, 0x08, 0x22, 0x12, 0x02, 0x22, 0x22, 0x22,
|
||||
0x3C, 0x22, 0x1E, 0x1C, 0x1E, 0x3E, 0x02, 0x3C, 0x22, 0x1C, 0x1C, 0x22,
|
||||
0x3E, 0x22, 0x22, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x1E, 0x1C, 0x1E, 0x1C, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3E, 0x3E,
|
||||
0x00, 0x3E, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x08, 0x22, 0x22, 0x22,
|
||||
0x22, 0x22, 0x20, 0x06, 0x02, 0x30, 0x00, 0x00, 0x22, 0x22, 0x22, 0x02,
|
||||
0x08, 0x22, 0x22, 0x22, 0x14, 0x14, 0x10, 0x06, 0x04, 0x30, 0x08, 0x00,
|
||||
0x1E, 0x22, 0x1E, 0x1C, 0x08, 0x22, 0x22, 0x2A, 0x08, 0x08, 0x08, 0x06,
|
||||
0x08, 0x30, 0x14, 0x00, 0x02, 0x2A, 0x0A, 0x20, 0x08, 0x22, 0x22, 0x2A,
|
||||
0x14, 0x08, 0x04, 0x06, 0x10, 0x30, 0x22, 0x00, 0x02, 0x12, 0x12, 0x22,
|
||||
0x08, 0x22, 0x14, 0x36, 0x22, 0x08, 0x02, 0x06, 0x20, 0x30, 0x00, 0x00,
|
||||
0x02, 0x2C, 0x22, 0x1C, 0x08, 0x1C, 0x08, 0x22, 0x22, 0x08, 0x3E, 0x3E,
|
||||
0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x08, 0x14, 0x14, 0x08, 0x06, 0x04, 0x08, 0x08, 0x08, 0x08, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x14, 0x14, 0x3C, 0x26, 0x0A, 0x08,
|
||||
0x04, 0x10, 0x2A, 0x08, 0x00, 0x00, 0x00, 0x20, 0x00, 0x08, 0x14, 0x3E,
|
||||
0x0A, 0x10, 0x0A, 0x08, 0x02, 0x20, 0x1C, 0x08, 0x00, 0x00, 0x00, 0x10,
|
||||
0x00, 0x08, 0x00, 0x14, 0x1C, 0x08, 0x04, 0x00, 0x02, 0x20, 0x08, 0x3E,
|
||||
0x00, 0x3E, 0x00, 0x08, 0x00, 0x08, 0x00, 0x3E, 0x28, 0x04, 0x2A, 0x00,
|
||||
0x02, 0x20, 0x1C, 0x08, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x14,
|
||||
0x1E, 0x32, 0x12, 0x00, 0x04, 0x10, 0x2A, 0x08, 0x08, 0x00, 0x00, 0x02,
|
||||
0x00, 0x08, 0x00, 0x14, 0x08, 0x30, 0x2C, 0x00, 0x08, 0x08, 0x08, 0x00,
|
||||
0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x1C, 0x08, 0x1C, 0x3E, 0x10, 0x3E, 0x38, 0x3E, 0x1C, 0x1C, 0x00, 0x00,
|
||||
0x10, 0x00, 0x04, 0x1C, 0x22, 0x0C, 0x22, 0x20, 0x18, 0x02, 0x04, 0x20,
|
||||
0x22, 0x22, 0x00, 0x00, 0x08, 0x00, 0x08, 0x22, 0x32, 0x08, 0x20, 0x10,
|
||||
0x14, 0x1E, 0x02, 0x10, 0x22, 0x22, 0x08, 0x08, 0x04, 0x3E, 0x10, 0x10,
|
||||
0x2A, 0x08, 0x18, 0x18, 0x12, 0x20, 0x1E, 0x08, 0x1C, 0x3C, 0x00, 0x00,
|
||||
0x02, 0x00, 0x20, 0x08, 0x26, 0x08, 0x04, 0x20, 0x3E, 0x20, 0x22, 0x04,
|
||||
0x22, 0x20, 0x08, 0x08, 0x04, 0x3E, 0x10, 0x08, 0x22, 0x08, 0x02, 0x22,
|
||||
0x10, 0x22, 0x22, 0x04, 0x22, 0x10, 0x00, 0x08, 0x08, 0x00, 0x08, 0x00,
|
||||
0x1C, 0x1C, 0x3E, 0x1C, 0x10, 0x1C, 0x1C, 0x04, 0x1C, 0x0E, 0x00, 0x04,
|
||||
0x10, 0x00, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x10, 0x10, 0x00, 0x7F, 0x00, 0x7F, 0x70, 0x00, 0x08, 0x00, 0x08, 0x08,
|
||||
0x7F, 0x40, 0x3F, 0x13, 0x08, 0x08, 0x00, 0x22, 0x40, 0x3F, 0x60, 0x18,
|
||||
0x04, 0x00, 0x08, 0x1C, 0x00, 0x40, 0x3F, 0x18, 0x36, 0x36, 0x02, 0x14,
|
||||
0x20, 0x5F, 0x7E, 0x07, 0x02, 0x00, 0x08, 0x2A, 0x00, 0x40, 0x3F, 0x1C,
|
||||
0x7F, 0x41, 0x06, 0x08, 0x11, 0x6C, 0x31, 0x00, 0x7F, 0x00, 0x08, 0x49,
|
||||
0x00, 0x44, 0x3F, 0x7E, 0x3F, 0x21, 0x0E, 0x08, 0x0A, 0x75, 0x79, 0x07,
|
||||
0x02, 0x00, 0x49, 0x08, 0x00, 0x46, 0x3F, 0x1C, 0x3F, 0x21, 0x1E, 0x14,
|
||||
0x04, 0x7B, 0x30, 0x0C, 0x04, 0x00, 0x2A, 0x08, 0x00, 0x7F, 0x3F, 0x18,
|
||||
0x7E, 0x4A, 0x36, 0x2A, 0x04, 0x7B, 0x3F, 0x08, 0x08, 0x00, 0x1C, 0x08,
|
||||
0x00, 0x06, 0x3F, 0x10, 0x36, 0x36, 0x42, 0x7F, 0x00, 0x7F, 0x02, 0x70,
|
||||
0x00, 0x2A, 0x08, 0x08, 0x00, 0x04, 0x3F, 0x6F, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x64, 0x40, 0x40, 0x00, 0x01, 0x08, 0x2A, 0x55, 0x00, 0x00, 0x40, 0x08,
|
||||
0x7F, 0x14, 0x7F, 0x01, 0x0C, 0x48, 0x48, 0x00, 0x01, 0x10, 0x55, 0x2A,
|
||||
0x3E, 0x00, 0x40, 0x1C, 0x00, 0x14, 0x40, 0x01, 0x1C, 0x08, 0x1C, 0x00,
|
||||
0x01, 0x20, 0x2A, 0x55, 0x41, 0x3F, 0x40, 0x3E, 0x00, 0x77, 0x40, 0x01,
|
||||
0x3F, 0x7F, 0x3E, 0x7F, 0x01, 0x7F, 0x55, 0x2A, 0x01, 0x40, 0x40, 0x7F,
|
||||
0x00, 0x00, 0x4C, 0x01, 0x1C, 0x3E, 0x7F, 0x00, 0x01, 0x20, 0x2A, 0x55,
|
||||
0x01, 0x40, 0x40, 0x3E, 0x00, 0x77, 0x4C, 0x01, 0x0C, 0x1C, 0x08, 0x00,
|
||||
0x01, 0x10, 0x55, 0x2A, 0x01, 0x40, 0x40, 0x1C, 0x00, 0x14, 0x40, 0x01,
|
||||
0x04, 0x48, 0x48, 0x00, 0x01, 0x08, 0x2A, 0x55, 0x7F, 0x7F, 0x40, 0x08,
|
||||
0x00, 0x14, 0x40, 0x01, 0x7B, 0x40, 0x40, 0x00, 0x7F, 0x00, 0x55, 0x2A,
|
||||
0x00, 0x00, 0x40, 0x00, 0x7F, 0x00, 0x7F, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x04, 0x00, 0x02, 0x00, 0x20, 0x00, 0x18, 0x00, 0x02, 0x08, 0x10, 0x02,
|
||||
0x0C, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x20, 0x00, 0x24, 0x00,
|
||||
0x02, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, 0x10, 0x1C, 0x1E, 0x3C,
|
||||
0x3C, 0x1C, 0x04, 0x1C, 0x1E, 0x0C, 0x18, 0x22, 0x08, 0x36, 0x1E, 0x1C,
|
||||
0x00, 0x20, 0x22, 0x02, 0x22, 0x22, 0x1E, 0x22, 0x22, 0x08, 0x10, 0x12,
|
||||
0x08, 0x2A, 0x22, 0x22, 0x00, 0x3C, 0x22, 0x02, 0x22, 0x3E, 0x04, 0x22,
|
||||
0x22, 0x08, 0x10, 0x0E, 0x08, 0x2A, 0x22, 0x22, 0x00, 0x22, 0x22, 0x02,
|
||||
0x22, 0x02, 0x04, 0x3C, 0x22, 0x08, 0x10, 0x12, 0x08, 0x2A, 0x22, 0x22,
|
||||
0x00, 0x3C, 0x1E, 0x3C, 0x3C, 0x3C, 0x04, 0x20, 0x22, 0x1C, 0x12, 0x22,
|
||||
0x1C, 0x22, 0x22, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C,
|
||||
0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38,
|
||||
0x08, 0x0E, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x0C, 0x08, 0x18, 0x1A, 0x2A, 0x1E, 0x3C, 0x3A, 0x3C,
|
||||
0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3E, 0x0C, 0x08, 0x18, 0x00, 0x14,
|
||||
0x22, 0x22, 0x06, 0x02, 0x04, 0x22, 0x22, 0x22, 0x14, 0x22, 0x10, 0x06,
|
||||
0x08, 0x30, 0x00, 0x2A, 0x22, 0x22, 0x02, 0x1C, 0x04, 0x22, 0x22, 0x2A,
|
||||
0x08, 0x22, 0x08, 0x0C, 0x08, 0x18, 0x00, 0x14, 0x1E, 0x3C, 0x02, 0x20,
|
||||
0x24, 0x32, 0x14, 0x2A, 0x14, 0x3C, 0x04, 0x0C, 0x08, 0x18, 0x00, 0x2A,
|
||||
0x02, 0x20, 0x02, 0x1E, 0x18, 0x2C, 0x08, 0x36, 0x22, 0x20, 0x3E, 0x38,
|
||||
0x08, 0x0E, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x1C, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x22, 0x04, 0x2C, 0x22, 0x22, 0x04, 0x22, 0x04, 0x00, 0x10, 0x04, 0x2C,
|
||||
0x22, 0x04, 0x22, 0x1C, 0x00, 0x08, 0x1A, 0x00, 0x00, 0x08, 0x00, 0x08,
|
||||
0x00, 0x08, 0x08, 0x1A, 0x00, 0x08, 0x00, 0x22, 0x08, 0x1C, 0x00, 0x1C,
|
||||
0x22, 0x22, 0x1C, 0x1C, 0x3C, 0x1C, 0x1C, 0x00, 0x1C, 0x1C, 0x22, 0x22,
|
||||
0x14, 0x08, 0x22, 0x22, 0x22, 0x22, 0x20, 0x20, 0x02, 0x22, 0x22, 0x1E,
|
||||
0x22, 0x22, 0x22, 0x1A, 0x22, 0x08, 0x26, 0x22, 0x22, 0x22, 0x3C, 0x3C,
|
||||
0x02, 0x3E, 0x3E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x3E, 0x08, 0x2A, 0x22,
|
||||
0x22, 0x22, 0x22, 0x22, 0x3C, 0x02, 0x02, 0x22, 0x22, 0x22, 0x32, 0x22,
|
||||
0x22, 0x08, 0x32, 0x22, 0x22, 0x22, 0x3C, 0x3C, 0x08, 0x3C, 0x3C, 0x22,
|
||||
0x1C, 0x1C, 0x2C, 0x1A, 0x22, 0x1C, 0x22, 0x1C, 0x1C, 0x1C, 0x00, 0x00,
|
||||
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x18, 0x3C, 0x04, 0x08, 0x08, 0x22, 0x3F, 0x7F, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x24, 0x02, 0x0A, 0x00, 0x00, 0x00, 0x3F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x1C, 0x04, 0x08,
|
||||
0x08, 0x00, 0x3F, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0E, 0x22, 0x00, 0x08, 0x08, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x04, 0x22, 0x00, 0x08, 0x04, 0x00, 0x39, 0x7F,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x1C, 0x00, 0x08,
|
||||
0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x3E, 0x20, 0x00, 0x08, 0x1C, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x7B, 0x7F,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
22
Source/LibAppleII/A2DiskDrive.h
Normal file
22
Source/LibAppleII/A2DiskDrive.h
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
@interface A2DiskDrive : NSObject <A2PrDiskDrive>
|
||||
{
|
||||
fd_t mOrigFD, // open FD to original disk image
|
||||
mWorkFD; // open FD to nybblized version
|
||||
@public
|
||||
NSString* mFilePath; // path to disk image file
|
||||
uint8_t mContent; // kA2DiskNone, etc.
|
||||
BOOL mDiskModified; // any writes since disk was loaded?
|
||||
|
||||
uint8_t* mTrackBase; // base of active track (circular buffer)
|
||||
unsigned mTheta, // current byte position in active track
|
||||
mTrackSize; // total number of bytes in track
|
||||
int mTrackIndex, // index of active track
|
||||
mTrackMax; // largest allowed track index
|
||||
}
|
||||
|
||||
- (id) InitUsingBuffer:(uint8_t [])buffer;
|
||||
- (BOOL) SeekTrack:(unsigned)reqTrack;
|
||||
+ (BOOL) ShouldShowFilename:(NSString*)path;
|
||||
|
||||
@end
|
460
Source/LibAppleII/A2DiskDrive.m
Normal file
460
Source/LibAppleII/A2DiskDrive.m
Normal file
@ -0,0 +1,460 @@
|
||||
/* class A2DiskDrive
|
||||
*/
|
||||
#import "LibAppleII-Priv.h"
|
||||
#import "A2DiskDrive.h"
|
||||
#import "A2DiskImages.h"
|
||||
|
||||
@implementation A2DiskDrive
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
enum
|
||||
{
|
||||
kSizeNIBTrack = 0x1A00, // 6656
|
||||
kSizeNB2Track = 0x18F0, // 6384
|
||||
kSizePhysTrack = kSizeNIBTrack, // should be 6350-6480 bytes
|
||||
|
||||
kGap3 = 16, // should be 16-24 bytes
|
||||
kGap2 = 6, // should be 5-10 bytes
|
||||
kSizePhysSector = kGap3 + (3+8+3) + kGap2 + (3+86+256+1+3),
|
||||
// kNumTracks = 35, // 35-40??
|
||||
kDefaultVolumeNum = 254,
|
||||
|
||||
kOpenRW = O_NONBLOCK | O_RDWR | O_EXLOCK,
|
||||
kOpenR = O_NONBLOCK | O_RDONLY,
|
||||
};
|
||||
|
||||
static struct
|
||||
{
|
||||
regex_t rexDigits,
|
||||
rexSuffixes5,
|
||||
rexSuffixes3;
|
||||
} g;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
enum { kFlags = REG_EXTENDED | REG_NOSUB | REG_ICASE };
|
||||
|
||||
if (self != [A2DiskDrive class])
|
||||
return; // ensures this routine executes no more than once
|
||||
|
||||
regcomp(&g.rexDigits, "[0-9]+", kFlags);
|
||||
regcomp(&g.rexSuffixes5, "2i?mg|dsk|[dp]o|n[iy]b", kFlags);
|
||||
regcomp(&g.rexSuffixes3, "2i?mg|img|hdv", kFlags);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)_WriteWorkingDiskToFile:(fd_t)fout:(BOOL)as2IMG
|
||||
{
|
||||
REWIND(fout);
|
||||
ftruncate(fout, 0);
|
||||
|
||||
if (as2IMG)
|
||||
{
|
||||
A2Header2IMG hdr =
|
||||
{
|
||||
.m2IMG = "2IMG",
|
||||
.mCreator = "CTKG",
|
||||
.mHeaderLength = NSSwapHostShortToLittle(64),
|
||||
.mVersion = NSSwapHostShortToLittle(1),
|
||||
.mFormat = 2, // NIB
|
||||
// .mPad1 = {0},
|
||||
.mVolNumber = 0,
|
||||
.mGotVolume = 0,
|
||||
// .mPad2 = 0,
|
||||
.mLocked = 0,
|
||||
.mNumBlocks = 0,
|
||||
.mDataPos = NSSwapHostLongToLittle(sizeof(A2Header2IMG)),
|
||||
.mDataLen = NSSwapHostLongToLittle(35L * 0x2000),
|
||||
.mCommentPos = 0,
|
||||
.mCommentLen = 0,
|
||||
.mAppDataPos = 0,
|
||||
.mAppDataLen = 0,
|
||||
// .mPad3 = {0},
|
||||
};
|
||||
write(fout, &hdr, sizeof(hdr));
|
||||
}
|
||||
|
||||
for (int t = 0; t < 35; ++t)
|
||||
{
|
||||
[self SeekTrack:t];
|
||||
write(fout, mTrackBase, kSizeNIBTrack);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static void EnnybSector(uint8_t* dest, //[kSizePhysSector]
|
||||
uint8_t src[256+2], int volume, int track, int sector)
|
||||
{/*
|
||||
Make a physical, nybblized sector out of a logical one. Input is
|
||||
taken from array _src_; output is deposited into array _dest_.
|
||||
*/
|
||||
#define ENCODE_44(BYTE) \
|
||||
dest[0] = 0xAA | (x=(BYTE))>>1; \
|
||||
dest[1] = 0xAA | x; dest += 2
|
||||
|
||||
#define TRIPLET(B0,B2) \
|
||||
dest[0] = B0; dest[1] = 0xAA; dest[2] = B2; dest += 3
|
||||
|
||||
static const uint8_t pbyte[] =
|
||||
{ // physical encoding of 6-bit logical values
|
||||
0x96,0x97,0x9A,0x9B,0x9D,0x9E,0x9F,0xA6,
|
||||
0xA7,0xAB,0xAC,0xAD,0xAE,0xAF,0xB2,0xB3,
|
||||
0xB4,0xB5,0xB6,0xB7,0xB9,0xBA,0xBB,0xBC,
|
||||
0xBD,0xBE,0xBF,0xCB,0xCD,0xCE,0xCF,0xD3,
|
||||
0xD6,0xD7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,
|
||||
0xDF,0xE5,0xE6,0xE7,0xE9,0xEA,0xEB,0xEC,
|
||||
0xED,0xEE,0xEF,0xF2,0xF3,0xF4,0xF5,0xF6,
|
||||
0xF7,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF
|
||||
};
|
||||
uint8_t x, ox;
|
||||
|
||||
dest += kGap3;
|
||||
TRIPLET(0xD5, 0x96);
|
||||
ENCODE_44(volume);
|
||||
ENCODE_44(track);
|
||||
ENCODE_44(sector);
|
||||
ENCODE_44(volume ^ track ^ sector);
|
||||
TRIPLET(0xDE, 0xEB);
|
||||
dest += kGap2;
|
||||
TRIPLET(0xD5, 0xAD);
|
||||
|
||||
for (int i = ox = 0; i < 86; ++i, ox = x)
|
||||
{
|
||||
x = "\0\x02\x01\x03"[ src [i] & 3] |
|
||||
"\0\x08\x04\x0C"[(src+ 86)[i] & 3] |
|
||||
"\0\x20\x10\x30"[(src+172)[i] & 3];
|
||||
dest[i] = pbyte[ox ^ x];
|
||||
}
|
||||
dest += 86;
|
||||
|
||||
for (int i = 0; i < 256; ++i, ox = x)
|
||||
dest[i] = pbyte[ox ^ (x = src[i] >> 2)];
|
||||
dest += 256;
|
||||
|
||||
*dest++ = pbyte[x];
|
||||
TRIPLET(0xDE, 0xEB);
|
||||
|
||||
#undef ENCODE_44
|
||||
#undef TRIPLET
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)_ImportDisk140K:(int)format:(int)volume
|
||||
{
|
||||
const uint8_t sectorMaps[][16] =
|
||||
{
|
||||
{0,7,14,6,13,5,12,4,11,3,10,2,9,1,8,15}, // DO
|
||||
{0,8,1,9,2,10,3,11,4,12,5,13,6,14,7,15}, // PO
|
||||
// {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}, // identical
|
||||
// {0,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1}, // reversed
|
||||
};
|
||||
enum { kGap1 = 0x2000 - 16*kSizePhysSector };
|
||||
const uint8_t* map;
|
||||
long base = TELL(mOrigFD);
|
||||
uint8_t lsec[256+2], // logical sector
|
||||
psec[kSizePhysSector]; // physical sector
|
||||
|
||||
lsec[256] = lsec[257] = 0;
|
||||
memset(psec, 0xFF, sizeof(psec));
|
||||
map = sectorMaps[format == kFmtPO or format == kFmt2IMG_PO];
|
||||
|
||||
for (int t = 0; t < 35; ++t) // 35 tracks of ...
|
||||
{
|
||||
for (int s = 0; s < 16; ++s) // 16 physical sectors
|
||||
{
|
||||
pread(mOrigFD, lsec, 256, base + 256L*(t*16 + map[s]));
|
||||
EnnybSector(psec, lsec, volume, t, s);
|
||||
write(mWorkFD, psec, kSizePhysSector);
|
||||
}
|
||||
A2WriteFiller(mWorkFD, 0xFF, kGap1);
|
||||
}
|
||||
|
||||
ftruncate(mWorkFD, 35*0x2000);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)_ImportDiskNIB:(uint32_t)rdTrackSize
|
||||
{
|
||||
enum { wrTrackSize = 0x2000 };
|
||||
uint8_t buf[2][wrTrackSize];
|
||||
|
||||
memset(buf[0], 0xFF, wrTrackSize);
|
||||
|
||||
for (int t = 35; --t >= 0;)
|
||||
{
|
||||
read(mOrigFD, buf[0], rdTrackSize);
|
||||
#if 0
|
||||
memcpy(buf[1], buf[0], wrTrackSize);
|
||||
|
||||
#endif
|
||||
write(mWorkFD, buf[0], wrTrackSize);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (BOOL)SeekTrack:(unsigned)reqTrack
|
||||
{/*
|
||||
Moves this drive's R/W head to the requested track, identified by a
|
||||
non-negative index. Returns whether successful. Fails if the index is
|
||||
outside the range of available tracks.
|
||||
*/
|
||||
if (reqTrack > mTrackMax) // then requested track out of range
|
||||
return NO;
|
||||
|
||||
// if (mTrackIndex != reqTrack)
|
||||
A2MemoryMap(mTrackBase, 0x2000, mWorkFD, reqTrack*0x2000);
|
||||
|
||||
// mTheta = 0; // always rewind??
|
||||
mTrackIndex = reqTrack;
|
||||
return YES;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)Unload
|
||||
{/*
|
||||
Empties this disk drive of any disk.
|
||||
*/
|
||||
if (mDiskModified and IS_OPEN(mOrigFD))
|
||||
{
|
||||
[self _WriteWorkingDiskToFile:mOrigFD:NO];
|
||||
[mFilePath SetFileCreator:'Ctkg' andType:'A2D5'];
|
||||
if ([mFilePath ExtensionMatches:&g.rexSuffixes5])
|
||||
[mFilePath SetFileExtension:@"nyb"];
|
||||
}
|
||||
|
||||
[self SeekTrack:0];
|
||||
|
||||
mContent = kA2DiskNone;
|
||||
mOrigFD = CLOSE(mOrigFD);
|
||||
mFilePath = [mFilePath Release];
|
||||
mTrackSize = 0x1A00;
|
||||
mTrackMax = 0;
|
||||
mTheta = 0;
|
||||
mDiskModified = NO;
|
||||
|
||||
memset(mTrackBase, 0xFF, mTrackSize);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (id)InitUsingBuffer:(uint8_t [/*0x2000*/])buffer
|
||||
{
|
||||
if (nil == (self = [super init]))
|
||||
return nil;
|
||||
|
||||
mTrackBase = buffer;
|
||||
mOrigFD = kBadFD;
|
||||
mWorkFD = A2OpenTempFile(35L * 0x2000);
|
||||
|
||||
if (not IS_OPEN(mWorkFD))
|
||||
return [self Release];
|
||||
|
||||
[self Unload];
|
||||
return self;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self Unload];
|
||||
close(mWorkFD);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (BOOL)_OpenDiskImageFile:(NSString*)fpath
|
||||
{
|
||||
const char* cfpath = [fpath fileSystemRepresentation];
|
||||
unsigned volumeNum = kDefaultVolumeNum,
|
||||
format = kFmtUnknown;
|
||||
struct stat st;
|
||||
long n;
|
||||
union {
|
||||
A2Header2IMG _2IMG;
|
||||
A2HeaderDiskCopy4 DC4;
|
||||
char bytes[512];
|
||||
} header;
|
||||
|
||||
/*-------------------------------------------------------
|
||||
Try opening the file for reading & writing -- if that
|
||||
fails, then just for reading -- and if that fails,
|
||||
give up.
|
||||
*/
|
||||
if (IS_OPEN(mOrigFD = open(cfpath, kOpenRW)))
|
||||
mContent = kA2DiskReadWrite;
|
||||
else if (IS_OPEN(mOrigFD = open(cfpath, kOpenR)))
|
||||
mContent = kA2DiskReadOnly;
|
||||
else // we can't open in any useful mode
|
||||
return NO;
|
||||
|
||||
if (fstat(mOrigFD, &st) != 0 or
|
||||
read(mOrigFD, &header, sizeof(header)) < sizeof(header) )
|
||||
return NO;
|
||||
|
||||
/*------------------------------------------------------
|
||||
Infer the file's format from it's size or header.
|
||||
*/
|
||||
n = st.st_size / 35;
|
||||
|
||||
if (n == 0x1000) // then DO or PO
|
||||
format = kFmtDO + [[fpath lowercaseString] hasSuffix:@".po"];
|
||||
else if (n == kSizeNIBTrack)
|
||||
format = kFmtNIB;
|
||||
else if (n == kSizeNB2Track)
|
||||
format = kFmtNB2;
|
||||
else // try lexing the header bytes to infer format
|
||||
format = A2GleanFileFormat(&header, sizeof(header));
|
||||
|
||||
if (format == kFmtUnknown // still? then test for HDV format
|
||||
and (st.st_size & 0x1FF) == 0
|
||||
and (st.st_size >> 9) >= 280
|
||||
and (st.st_size >> 9) <= 0xFFFF
|
||||
) format = kFmtHDV;
|
||||
|
||||
mTrackMax = 35 - 1;
|
||||
mTrackSize = kSizePhysTrack;
|
||||
mFilePath = [fpath retain];
|
||||
REWIND(mOrigFD);
|
||||
REWIND(mWorkFD);
|
||||
|
||||
// NSLog(@"disk file format = %d", format); //!!
|
||||
|
||||
switch (format)
|
||||
{
|
||||
default: return NO;
|
||||
|
||||
case kFmtDO: // fall thru
|
||||
case kFmtPO: [self _ImportDisk140K:format:volumeNum]; break;
|
||||
case kFmtNB2: // fall thru
|
||||
case kFmtNIB: [self _ImportDiskNIB:(st.st_size / 35)]; break;
|
||||
|
||||
case kFmt2IMG_PO: case kFmt2IMG_DO: case kFmt2IMG_NIB:
|
||||
if (header._2IMG.mLocked & 0x80)
|
||||
mContent = kA2DiskReadOnly;
|
||||
if (header._2IMG.mGotVolume & 1)
|
||||
volumeNum = header._2IMG.mVolNumber;
|
||||
lseek(mOrigFD,
|
||||
NSSwapLittleLongToHost(header._2IMG.mDataPos),
|
||||
SEEK_SET);
|
||||
|
||||
if (format == kFmt2IMG_NIB)
|
||||
[self _ImportDiskNIB:kSizeNIBTrack];
|
||||
else
|
||||
[self _ImportDisk140K:format:volumeNum];
|
||||
break;
|
||||
#if 0
|
||||
case kFmtDiskCopy4:
|
||||
lseek(mOrigFD, 84, SEEK_SET);
|
||||
mTrackMax = 80 - 1;
|
||||
break;
|
||||
|
||||
case kFmtHDV: case kFmt2IMG_HDV: //??
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (BOOL)Load:(NSString*)fpath //!!
|
||||
{/*
|
||||
Attempts to load the specified disk image file into this disk drive.
|
||||
Returns whether successful.
|
||||
|
||||
Any disk currently loaded is unloaded first. Passing nil has the
|
||||
effect of unloading only, and is automatically successful.
|
||||
*/
|
||||
[self Unload];
|
||||
if (fpath == nil)
|
||||
return YES;
|
||||
|
||||
// errno = 0;
|
||||
// fpath = [fpath stringByStandardizingPath]; //??
|
||||
if (not [self _OpenDiskImageFile:fpath])
|
||||
{
|
||||
[self Unload];
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (NSString*)Label
|
||||
{/*
|
||||
Returns a name for the currently loaded disk that's appropriate to
|
||||
display in the user interface. Common file extensions are omitted.
|
||||
Returns an empty string if the drive is empty.
|
||||
*/
|
||||
if (mFilePath == nil)
|
||||
return @"";
|
||||
|
||||
NSString* name = [mFilePath lastPathComponent];
|
||||
|
||||
return [[name pathExtension] Matches:&g.rexDigits]? name :
|
||||
[name stringByDeletingPathExtension];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
+ (BOOL)ShouldShowFilename:(NSString*)path
|
||||
{
|
||||
#if 0
|
||||
NSString *app, *type;
|
||||
[[NSWorkspace sharedWorkspace] getInfoForFile:path
|
||||
application:&app type:&type];
|
||||
NSLog(@"file type = '%@'", type);
|
||||
#endif
|
||||
|
||||
struct stat st;
|
||||
|
||||
if (not [path Stat:&st]) // can't stat? then reject
|
||||
return NO;
|
||||
/*
|
||||
if it's a bundle/package
|
||||
return NO
|
||||
else if it's a directory
|
||||
return YES
|
||||
else if it's not a readable file
|
||||
return NO
|
||||
else if we like the extension
|
||||
return YES
|
||||
else return whether we like the file size
|
||||
*/
|
||||
if (st.st_mode & S_IFDIR) // pass non-bundle directories
|
||||
return YES; //return [NSBundle bundleWithPath:path] == nil;
|
||||
|
||||
if (not [path IsReadableFile]) // reject unreadable files
|
||||
return NO;
|
||||
if ([path ExtensionMatches:&g.rexSuffixes5])
|
||||
return YES;
|
||||
|
||||
off_t tsz = st.st_size / 35;
|
||||
|
||||
if (tsz == 0x1000)
|
||||
return YES;
|
||||
|
||||
return tsz >= kSizeNB2Track and tsz <= kSizeNIBTrack;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (unsigned)Content
|
||||
{ return mContent; }
|
||||
// Returns this drive's current content (kA2DiskNone, etc.)
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
50
Source/LibAppleII/A2DiskImages.h
Normal file
50
Source/LibAppleII/A2DiskImages.h
Normal file
@ -0,0 +1,50 @@
|
||||
/* A2DiskImages.h
|
||||
|
||||
Header formats of Apple II disk image files.
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{/*
|
||||
Header format of 2IMG disk images. All integer fields are
|
||||
little-endian. Positions and lengths are in bytes.
|
||||
*/
|
||||
char m2IMG[4], // "2IMG"
|
||||
mCreator[4]; // "CTKG", or other producer
|
||||
uint16_t mHeaderLength, // 64
|
||||
mVersion; // 0 or 1
|
||||
|
||||
uint8_t mFormat, // 0=DO, 1=PO, 2=NIB
|
||||
mPad1[3],
|
||||
mVolNumber,
|
||||
mGotVolume, // bit 0
|
||||
mPad2,
|
||||
mLocked; // bit 7
|
||||
|
||||
uint32_t mNumBlocks, // for PO only
|
||||
|
||||
mDataPos, mDataLen,
|
||||
mCommentPos, mCommentLen,
|
||||
mAppDataPos, mAppDataLen;
|
||||
|
||||
char mPad3[16]; // pad out to 64 bytes
|
||||
|
||||
} A2Header2IMG;
|
||||
|
||||
|
||||
typedef struct
|
||||
{/*
|
||||
Header format of DiskCopy 4.x disk images. All integer fields are
|
||||
big-endian. Sizes are in bytes. Data blocks start at offset 84
|
||||
from the file's beginning. Tag data can be ignored.
|
||||
*/
|
||||
char mDiskName[64]; // Pascal string
|
||||
|
||||
uint32_t mDataSize, mTagSize,
|
||||
mDataChecksum, mTagChecksum;
|
||||
|
||||
uint8_t mDiskFormat,
|
||||
mFormatByte,
|
||||
mPrivate[2];
|
||||
|
||||
} A2HeaderDiskCopy4;
|
||||
|
10
Source/LibAppleII/A2T.h
Normal file
10
Source/LibAppleII/A2T.h
Normal file
File diff suppressed because one or more lines are too long
307
Source/LibAppleII/Globals.l
Normal file
307
Source/LibAppleII/Globals.l
Normal file
@ -0,0 +1,307 @@
|
||||
/* Globals.l (.m)
|
||||
*/
|
||||
%option 8bit never-interactive noyywrap prefix="A2Globals_"
|
||||
%{
|
||||
#import "LibAppleII-Priv.h"
|
||||
#import "A2DiskDrive.h"
|
||||
|
||||
int yylex(void);
|
||||
%}
|
||||
|
||||
ANY [\0-\xFF]
|
||||
BSC [^F]*FiLeStArTfIlEsTaRt[ \t]*[\r\n]
|
||||
NIB [\x96-\xFF]+
|
||||
DiskCopy4 [\0-\x3F]{ANY}{79}[\0-\3][\x12\x22\x24]\1\0
|
||||
_2IMG 2IMG{ANY}{4}\x40\0[\0\1]\0
|
||||
_2IMG_PO {_2IMG}\1{ANY}{7}(\x18\1|\0\0)\0\0
|
||||
_2IMG_HDV {_2IMG}\1{ANY}{7}([\x19-\xFF]\1|{ANY}[\2-\xFF])\0\0
|
||||
|
||||
%%
|
||||
|
||||
{_2IMG}\0 return kFmt2IMG_DO;
|
||||
{_2IMG}\2 return kFmt2IMG_NIB;
|
||||
{_2IMG_PO} return kFmt2IMG_PO;
|
||||
{_2IMG_HDV} return kFmt2IMG_HDV;
|
||||
|
||||
N\xF5F\xE9l\xE5 return kFmtSHK;
|
||||
\x1F\x8B[\0-\x9] return kFmtGZip;
|
||||
BZh return kFmtBZip;
|
||||
{DiskCopy4} return kFmtDiskCopy4;
|
||||
{BSC} return kFmtBSC;
|
||||
\x0A\x47\x4C return kFmtBinaryII;
|
||||
{NIB} return kFmtNIB;
|
||||
|
||||
{ANY} return kFmtUnknown;
|
||||
|
||||
%%
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
struct A2Globals A2G =
|
||||
{
|
||||
.defaultModel = kA2ModelIIp,
|
||||
.defaultExtraRAM = 0, // sensible values: 0, 18-21
|
||||
|
||||
.standardColors = // ... in a 12-bit RGB format
|
||||
{
|
||||
0x000, 0xC03, 0x00A, 0xB0F,
|
||||
0x060, 0x666, 0x06C, 0x0CF,
|
||||
0x630, 0xF60, 0x666, 0xF9F,
|
||||
0x0F0, 0xFF0, 0x3F9, 0xFFF,
|
||||
},
|
||||
|
||||
/*
|
||||
.flagsSSC1 = 0xED78,
|
||||
.flagsSSC2 = 0xED78,
|
||||
.flagsEpsonRX80 = 0,
|
||||
.printerDataMask = 0x7F,
|
||||
*/
|
||||
};
|
||||
|
||||
struct A2PrivateTables A2T =
|
||||
{
|
||||
#include "A2T.h"
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
unsigned A2GleanFileFormat(const void* header, size_t size)
|
||||
{/*
|
||||
Infers a file's format from its header bytes.
|
||||
*/
|
||||
YY_BUFFER_STATE bufState = yy_scan_bytes(header, size);
|
||||
unsigned format = yylex();
|
||||
|
||||
yy_delete_buffer(bufState);
|
||||
return format;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
unsigned A2Random16(void)
|
||||
{/*
|
||||
Returns a pseudo-random 16-bit integer. The algorithm here is an
|
||||
embellishment of one described in:
|
||||
|
||||
"Efficient and Portable Combined Random Number Generators"
|
||||
by Pierre L'Ecuyer
|
||||
Communications of the ACM
|
||||
Vol 31, #6, June 1988
|
||||
*/
|
||||
enum {
|
||||
m = 2147483563, a = 40014, q = 53668, r = 12211,
|
||||
// m = 2147483399, a = 40692, q = 52774, r = 3791,
|
||||
};
|
||||
static long v = 1;
|
||||
ldiv_t d = ldiv(v, q);
|
||||
|
||||
if ((v = d.rem * a - d.quot * r) <= 0)
|
||||
v += m;
|
||||
return (v ^ v>>16) & 0xFFFF;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void* A2MemoryMap(void* addr, size_t size, fd_t fd, off_t foff)
|
||||
{/*
|
||||
Maps or re-maps a range of the process address space to a range within
|
||||
an open file, or to new memory if no file is given. If passed a file,
|
||||
we assume the file was opened for both reading and writing.
|
||||
|
||||
Returns the same results as 'mmap'.
|
||||
*/
|
||||
int flags = MAP_SHARED // or MAP_PRIVATE??
|
||||
| (fd < 0? MAP_ANON : MAP_FILE);
|
||||
|
||||
if (addr != nil)
|
||||
{
|
||||
flags |= MAP_FIXED;
|
||||
// msync(addr, size, MS_SYNC + MS_INVALIDATE); // need??
|
||||
munmap(addr, size);
|
||||
}
|
||||
return mmap(addr, size, PROT_READ + PROT_WRITE, flags, fd, foff);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
fd_t A2OpenTempFile(size_t size)
|
||||
{/*
|
||||
Creates and opens a temporary file for reading and writing. Also sets
|
||||
the file's size fo _size_, if non-zero. Returns a valid file descriptor
|
||||
on success, or an invalid one (< 0) on failure.
|
||||
*/
|
||||
FILE* fptr;
|
||||
fd_t fd;
|
||||
|
||||
if (NULL == (fptr = tmpfile())) // then failed to create file
|
||||
return kBadFD;
|
||||
|
||||
fd = dup(fileno(fptr));
|
||||
fclose(fptr);
|
||||
if (fd < 0 or size < 1 or ftruncate(fd, size) == 0)
|
||||
return fd;
|
||||
|
||||
// Reaching here: file was created successfully, but the resize failed.
|
||||
return CLOSE(fd);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void A2WriteFiller(fd_t fout, char fillValue, size_t reps)
|
||||
{/*
|
||||
Writes fill bytes to the given file.
|
||||
*/
|
||||
enum { kChunk = 1<<9 };
|
||||
char buf[kChunk];
|
||||
|
||||
memset(buf, fillValue, kChunk);
|
||||
for (; reps >= kChunk; reps -= kChunk)
|
||||
write(fout, buf, kChunk);
|
||||
if (reps > 0)
|
||||
write(fout, buf, reps);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void A2WriteEntireFile(fd_t fout, fd_t fin)
|
||||
{/*
|
||||
Writes all the remaining bytes from one open file to another.
|
||||
*/
|
||||
char buf[1<<9];
|
||||
|
||||
for (int n; (n = read(fin, buf, sizeof(buf))) > 0;)
|
||||
write(fout, buf, n);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
BOOL A2AppendResourceFile(fd_t fout, NSString* resName)
|
||||
{/*
|
||||
Searches the application bundle for a given resource file, then
|
||||
appends its content to the given destination file.
|
||||
*/
|
||||
NSString* fpath;
|
||||
fd_t fin;
|
||||
|
||||
fpath = [resName PathForResource];
|
||||
if (fpath == nil)
|
||||
return NO;
|
||||
|
||||
fin = open([fpath fileSystemRepresentation], O_RDONLY);
|
||||
if (fin < 0)
|
||||
return NO;
|
||||
|
||||
A2WriteEntireFile(fout, fin);
|
||||
close(fin);
|
||||
return YES;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
void A2DumpArray(const char* name, const void* arr, size_t sz, int type)
|
||||
{/*
|
||||
Dumps an array of integers to a text file, in the format of C static
|
||||
initializers.
|
||||
*/
|
||||
static FILE* fout = NULL;
|
||||
char* p = (char*)arr;
|
||||
int esz = abs(type);
|
||||
|
||||
if (fout == NULL)
|
||||
fout = fopen(
|
||||
[[@"~/Desktop/A2T.h" stringByExpandingTildeInPath]
|
||||
fileSystemRepresentation], "w");
|
||||
|
||||
fprintf(fout, ".%s = { ", name);
|
||||
|
||||
for (long n = sz/esz; --n >= 0; p += esz)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case 1: fprintf(fout, "%u," , *(uint8_t *)p); break;
|
||||
case -1: fprintf(fout, "%d," , *(int8_t *)p); break;
|
||||
case 2: fprintf(fout, "%u," , *(uint16_t*)p); break;
|
||||
case -2: fprintf(fout, "%d," , *(int16_t *)p); break;
|
||||
case 4: fprintf(fout, "%lu,", *(uint32_t*)p); break;
|
||||
case -4: fprintf(fout, "%ld,", *(int32_t *)p); break;
|
||||
}
|
||||
}
|
||||
fprintf(fout, " },\n");
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
unsigned A2HitIWM(A2IWM* iwm, unsigned ea, unsigned d)
|
||||
{
|
||||
#define INC_THETA \
|
||||
if ((dr->mTheta += 1) >= dr->mTrackSize) \
|
||||
dr->mTheta = 0
|
||||
#define WRITING (ea & 32)
|
||||
|
||||
enum
|
||||
{
|
||||
DFLAG(0, CA0) DFLAG(4, Spinning)
|
||||
DFLAG(1, CA1) DFLAG(5, ActiveDrive)
|
||||
DFLAG(2, CA2) DFLAG(6, Q6)
|
||||
DFLAG(3, LSTRB) DFLAG(7, Q7)
|
||||
|
||||
kfQ67 = kfQ6 | kfQ7,
|
||||
};
|
||||
|
||||
unsigned f = iwm->flags;
|
||||
A2DiskDrive* dr = iwm->drive[f>>ksActiveDrive & 1];
|
||||
|
||||
if (ea & 1) // we're hitting an odd address
|
||||
{
|
||||
f |= 1U << (ea >> 1 & 7);
|
||||
|
||||
if ((ea & 8) == 0) // then stepper motor moves, maybe
|
||||
{
|
||||
int ti = dr->mTrackIndex,
|
||||
tiDelta = "=<><>=<>"[ea&6 | ti&1] - '=';
|
||||
|
||||
if (tiDelta != 0)
|
||||
[dr SeekTrack:(ti + tiDelta)];
|
||||
}
|
||||
|
||||
if (WRITING and (f & kfQ67) == kfQ67)
|
||||
{
|
||||
if (not (f & kfSpinning))
|
||||
iwm->modeReg = d;
|
||||
else if (dr->mContent == kA2DiskReadWrite)
|
||||
{
|
||||
dr->mDiskModified = YES;
|
||||
dr->mTrackBase[dr->mTheta] = d | 0x80;
|
||||
INC_THETA;
|
||||
}
|
||||
}
|
||||
}
|
||||
else // we're hitting an even address
|
||||
{
|
||||
f &= ~(1U << (ea >> 1 & 7));
|
||||
|
||||
if (not WRITING)
|
||||
{
|
||||
if ((f & kfQ67) == 0) // return disk data
|
||||
{
|
||||
d = dr->mTrackBase[dr->mTheta];
|
||||
INC_THETA;
|
||||
}
|
||||
else if (f & kfQ6) // return status reg
|
||||
d = (dr->mContent == kA2DiskReadOnly)<<7
|
||||
| (f&kfSpinning)<<1
|
||||
| (iwm->modeReg & 0x1F);
|
||||
else // return handshake reg
|
||||
d = 0xC0;
|
||||
}
|
||||
}
|
||||
|
||||
if (f & kfSpinning)
|
||||
iwm->lights = kLightSustain | (1 + (f>>ksActiveDrive & 1));
|
||||
iwm->flags = f;
|
||||
return d;
|
||||
|
||||
#undef INC_THETA
|
||||
#undef WRITING
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
166
Source/LibAppleII/LibAppleII-Priv.h
Normal file
166
Source/LibAppleII/LibAppleII-Priv.h
Normal file
@ -0,0 +1,166 @@
|
||||
/* LibAppleII-Priv.h
|
||||
|
||||
Private definitions for source files in the LibAppleII library. Client
|
||||
applications should normally ignore this header. The public API is all
|
||||
defined in "LibAppleII.h".
|
||||
*/
|
||||
#import "LibAppleII.h"
|
||||
#import "MyUtils.h"
|
||||
|
||||
#define LENGTH(ARRAY_1D) \
|
||||
( sizeof(ARRAY_1D) / sizeof(ARRAY_1D[0]) )
|
||||
// number of elements in a one-dimensional array
|
||||
|
||||
#define DFLAG(SHIFT, NAME) \
|
||||
ks##NAME = (SHIFT), kf##NAME = 1U << ks##NAME,
|
||||
// defines bit-flag constants with ease
|
||||
|
||||
#define IS_OPEN(FD) ((FD) >= 0)
|
||||
#define CLOSE(FD) (close(FD), -1)
|
||||
#define REWIND(FD) lseek((FD), 0, SEEK_SET)
|
||||
#define TELL(FD) lseek((FD), 0, SEEK_CUR)
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Handy integer constants.
|
||||
|
||||
enum
|
||||
{
|
||||
//-------------------------------------------------------------
|
||||
// File format identifiers
|
||||
|
||||
kFmtUnknown = 0,
|
||||
|
||||
kFmtDO, kFmtPO, kFmtNIB, kFmtHDV,
|
||||
kFmt2IMG_DO, kFmt2IMG_PO, kFmt2IMG_NIB, kFmt2IMG_HDV,
|
||||
|
||||
kFmtGZip, kFmtBZip,
|
||||
kFmtSHK, kFmtBSC, kFmtBinaryII,
|
||||
kFmtNB2, kFmtDiskCopy4, // kFmtCopyIIPlus, kFmtFDI,
|
||||
|
||||
//-------------------------------------------------------------
|
||||
// Apple II soft-switch flags (used in A2Computer field 'mFlags').
|
||||
|
||||
DFLAG( 0, TEXT) // flags affecting video display
|
||||
DFLAG( 1, MIXED)
|
||||
DFLAG( 2, 80COL)
|
||||
DFLAG( 3, SINGRES) // (inverse of "DHIRES" flag)
|
||||
DFLAG( 4, HIRESv)
|
||||
DFLAG( 5, PAGE2v)
|
||||
DFLAG( 6, 80STOREv)
|
||||
DFLAG( 7, ALTCHAR)
|
||||
|
||||
DFLAG( 8, RAMWRT) // flags affecting memory writes
|
||||
DFLAG( 9, LCWRTlo)
|
||||
DFLAG(10, LCWRThi)
|
||||
|
||||
DFLAG(11, ALTZP) // flags affecting memory reads and writes
|
||||
DFLAG(12, LCBANK2)
|
||||
DFLAG(13, HIRESm)
|
||||
DFLAG(14, PAGE2m)
|
||||
DFLAG(15, 80STOREm)
|
||||
|
||||
DFLAG(16, RAMRD) // flags affecting memory reads
|
||||
DFLAG(17, LCRD)
|
||||
DFLAG(18, CXROM)
|
||||
DFLAG(19, C3ROM)
|
||||
DFLAG(20, HotSlot)
|
||||
// DFLAG(21, HotSlot1)
|
||||
// DFLAG(22, HotSlot2)
|
||||
|
||||
ksWMap = ksRAMWRT, kmWMap = (1 << (3+5)) - 1,
|
||||
ksRMap = ksALTZP, kmRMap = (1 << (5+7)) - 1,
|
||||
|
||||
DFLAG(28, XYMASK) // IIc flags
|
||||
DFLAG(29, VBLMASK)
|
||||
DFLAG(30, X0EDGE)
|
||||
DFLAG(31, Y0EDGE)
|
||||
|
||||
//-------------------------------------------------------------
|
||||
// Miscellany
|
||||
|
||||
DFLAG(0, HaltNoPower) // flags for 'mHalts' field of A2Computer
|
||||
DFLAG(1, HaltReset)
|
||||
|
||||
kTapRatio = 5,
|
||||
kFilterRes = 128,
|
||||
kFilterSize = kFilterRes * kTapRatio,
|
||||
|
||||
kBadFD = -1,
|
||||
kLightSustain = 5 << 4,
|
||||
k64KB = 1UL << 16,
|
||||
// k1MB = 1UL << 20,
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Type definitions.
|
||||
|
||||
typedef int fd_t; // file descriptor
|
||||
typedef uint32_t PixelGroup; // a run of four 8-bit pixels
|
||||
typedef struct A2IWM A2IWM;
|
||||
//typedef struct A2PSG A2PSG;
|
||||
|
||||
typedef struct A2Memory
|
||||
{
|
||||
uint8_t
|
||||
RAM[8][2][0x2000], // main and aux RAM (64 KB each)
|
||||
ROM[2][0x4000], // up to 2 banks of ROM
|
||||
|
||||
WOM[0x800], // write-only memory (for ROM writes)
|
||||
altSlotROM[0x800],
|
||||
pad_[0x1000], // pad struct up to next 0x2000 boundary
|
||||
|
||||
diskBuffers[4][0x2000];
|
||||
|
||||
} A2Memory;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
extern struct A2PrivateTables
|
||||
{
|
||||
uint8_t tPHP[1 << 12];
|
||||
uint16_t tPLP[0x100],
|
||||
tROL[0x100 << 3],
|
||||
tROR[0x100 << 3];
|
||||
int16_t tADC [0xC40], tSBC [0xC40], // for 65c02
|
||||
tADCo[0xC40], tSBCo[0xC40]; // for 6502
|
||||
int8_t rmaps[kmRMap+1][0x80+1],
|
||||
wmaps[kmWMap+1][0x80+1];
|
||||
struct {
|
||||
uint32_t flat;
|
||||
uint32_t delta[kFilterSize];
|
||||
} audio;
|
||||
struct {
|
||||
uint16_t T[3][8][0x100]; // [charset][row][screen code] -> 14b
|
||||
uint32_t Hs[0x100]; // 8b -> 14b
|
||||
} vidBits;
|
||||
struct {
|
||||
PixelGroup Ts[1<<(2*2)], Hs[1<<(5*2)], G[16],
|
||||
Td[1<<(4*2)], Hd[1<<(6+3)];
|
||||
} vidPix;
|
||||
|
||||
uint8_t curTime[32];
|
||||
|
||||
} A2T;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Prototypes of global functions.
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
BOOL A2AppendResourceFile(fd_t fout, NSString* resName);
|
||||
void A2DumpArray(const char*, const void*, size_t, int);
|
||||
unsigned A2GleanFileFormat(const void* header, size_t size);
|
||||
unsigned A2HitIWM(A2IWM* iwm, unsigned ea, unsigned d);
|
||||
void* A2MemoryMap(void* addr, size_t size, fd_t fd, off_t foff);
|
||||
fd_t A2OpenTempFile(size_t size);
|
||||
unsigned A2Random16(void);
|
||||
void A2WriteEntireFile(fd_t fout, fd_t fin);
|
||||
void A2WriteFiller(fd_t fout, char fillValue, size_t reps);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
//---------------------------------------------------------------------------
|
240
Source/LibAppleII/LibAppleII.h
Normal file
240
Source/LibAppleII/LibAppleII.h
Normal file
@ -0,0 +1,240 @@
|
||||
/* LibAppleII.h
|
||||
|
||||
The one and only public interface file for the LibAppleII library.
|
||||
*/
|
||||
#import <Foundation/Foundation.h>
|
||||
//mport <AppKit/AppKit.h>
|
||||
#import <stdint.h>
|
||||
#import <stdio.h>
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Handy integer constants.
|
||||
|
||||
enum
|
||||
{
|
||||
kA2ModelNone = 0, // Apple II model codes:
|
||||
kA2ModelIIo, // the original Apple ][
|
||||
kA2ModelIIp, // the ][+
|
||||
kA2ModelIIe,
|
||||
kA2ModelIIc,
|
||||
kA2ModelIIcp, // the //c+ (not working yet!!)
|
||||
// kA2ModelIII,
|
||||
// kA2ModelIIIp,
|
||||
kA2NumModels,
|
||||
|
||||
kA2DiskNone = 0, // possible content of a disk drive
|
||||
kA2DiskReadOnly,
|
||||
kA2DiskReadWrite,
|
||||
|
||||
kA2PFPlain = 0, // printer I/O filters
|
||||
kA2PFEpsonToPS,
|
||||
// kA2PFScribeToPS,
|
||||
kA2PFVerbatim,
|
||||
|
||||
kfA2LightDDrive0 = 1, // indicator-light states, as returned
|
||||
kfA2LightDDrive1 = 1<<1, // from '-Lights'
|
||||
kfA2LightDDrive2 = 1<<2,
|
||||
kfA2LightDDrive3 = 1<<3,
|
||||
kfA2LightPrinter = 1<<4,
|
||||
|
||||
kfA2Button0 = 1, // button & mod-key states
|
||||
kfA2Button1 = 1<<1,
|
||||
kfA2Button2 = 1<<2,
|
||||
kfA2ButtonMouse = 1<<3,
|
||||
kfA2AnyKeyDown = 1<<4,
|
||||
kfA2KeyOpenApple = kfA2Button0,
|
||||
kfA2KeySolidApple = kfA2Button1,
|
||||
kfA2KeyShift = kfA2Button2,
|
||||
|
||||
kA2ScreenWidth = 4+560+4, // the pixel width and
|
||||
kA2ScreenHeight = 192*2, // height of a rendered screen image
|
||||
kA2PaddleRange = 3000, // largest useful paddle value
|
||||
kA2SamplesPerStep = 364, // audio samples per emulation step
|
||||
// kA2SamplesPerSec = 22050, // audio samples per second
|
||||
|
||||
#if 0
|
||||
kA2NaUSA = 0, // nation codes (not used yet)
|
||||
kA2NaFrance,
|
||||
kA2NaGermany,
|
||||
kA2NaUK,
|
||||
kA2NaDenmark,
|
||||
kA2NaSweden,
|
||||
kA2NaItaly,
|
||||
kA2NaSpain,
|
||||
kA2NaJapan,
|
||||
kA2NaNorway,
|
||||
#endif
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
extern struct A2Globals
|
||||
{
|
||||
unsigned buttons; // current button & mod-key states
|
||||
unsigned paddle[2]; // current paddle values
|
||||
BOOL timeInGMT; // clock times are in GMT?
|
||||
BOOL hearC02x; // hits on $C02x are audible?
|
||||
|
||||
uint8_t defaultModel;
|
||||
uint8_t defaultExtraRAM;
|
||||
const uint16_t standardColors[16];
|
||||
|
||||
// uint8_t pixels[1<<8][1<<10];
|
||||
// uint16_t dswitchSSC[2], // DIP switch settings
|
||||
// dswitchEpsonRX80;
|
||||
// BOOL keypadControlsPaddles;
|
||||
|
||||
} A2G;
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Apple II peripherals.
|
||||
|
||||
@class A2DiskDrive;
|
||||
|
||||
|
||||
@protocol A2PrDiskDrive
|
||||
|
||||
- (NSString*) Label;
|
||||
- (unsigned) Content;
|
||||
- (BOOL) Load:(NSString*)fpath;
|
||||
- (void) Unload;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#if 0
|
||||
@protocol A2PrPrinter
|
||||
|
||||
- (long) SizeOfSession;
|
||||
- (void) ClearSession;
|
||||
- (BOOL) SaveSessionAs:(unsigned)filter toFile:(NSString*)fpath;
|
||||
|
||||
@end
|
||||
#endif
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@interface A2Computer : NSResponder // <NSCoding>
|
||||
{
|
||||
//----------------- Computer State -----------------
|
||||
|
||||
uint8_t mA, mX, mY, mS, // some 6502 registers
|
||||
mI, // 6502 I flag (= 0 or 4)
|
||||
mModel, // model identifier code
|
||||
mHalts; // flags to block emulation
|
||||
uint16_t mPC, mP; // PC and pseudo-P registers
|
||||
uint32_t mFlags, // flags and soft-switches
|
||||
mMutableFlags, // flags this model may change
|
||||
mCycles, // CPU cycle count
|
||||
mWhenC07x; // time when $C07x last referenced
|
||||
|
||||
struct A2Memory* mMemory; // struct containing all our memory
|
||||
unsigned mMemorySize; // size of allocated memory
|
||||
int16_t *mTblADC, // this model's ADC and SBC tables
|
||||
*mTblSBC;
|
||||
|
||||
uint8_t mVideoFlags[262]; // video flags for each scanline
|
||||
|
||||
//----------------- Peripherals -----------------
|
||||
|
||||
struct { // keyboard input queue
|
||||
BOOL hitRecently;
|
||||
uint8_t head, tail, buf[256];
|
||||
} mKeyQ;
|
||||
|
||||
struct { // printer
|
||||
FILE* session;
|
||||
uint8_t lights, reg[16];
|
||||
} mPrinter;
|
||||
|
||||
struct {
|
||||
int index;
|
||||
} mClock;
|
||||
|
||||
struct { // optional "Slinky" RAM card
|
||||
uint32_t pos, mask;
|
||||
uint8_t *base, nowhere;
|
||||
} mSlinky;
|
||||
|
||||
struct A2IWM { // two IWM controllers, with two drives each
|
||||
A2DiskDrive* drive[2];
|
||||
uint8_t flags, modeReg, lights;
|
||||
} mIWM[2];
|
||||
|
||||
#if 0
|
||||
struct A2PSG { // stereo Mockingboard chips
|
||||
uint16_t freqA, freqB, freqC,
|
||||
periodEnv;
|
||||
uint8_t freqNG, disable, shapeEnv,
|
||||
levelA, levelB, levelC;
|
||||
} mPSG[2];
|
||||
#endif
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// The various methods of A2Computer, grouped by category.
|
||||
|
||||
@interface A2Computer (Audio)
|
||||
|
||||
+ (void) _InitAudio;
|
||||
- (void) _DefaultAudio:(uint8_t [])audioOut :(unsigned)nSamples;
|
||||
+ (void) SetAudioVolume:(unsigned)volume;
|
||||
|
||||
@end
|
||||
|
||||
@interface A2Computer (CPU)
|
||||
|
||||
+ (void) _InitCPU;
|
||||
- (void) RunForOneStep:(uint8_t [])audioOut;
|
||||
|
||||
@end
|
||||
|
||||
@interface A2Computer (Printing)
|
||||
|
||||
+ (void) _InitPrinting;
|
||||
- (long) SizeOfPrintSession;
|
||||
- (void) ClearPrintSession;
|
||||
- (BOOL) SavePrintSessionAs:(unsigned)filter toFile:(NSString*)fpath;
|
||||
|
||||
@end
|
||||
|
||||
@interface A2Computer (ROM)
|
||||
|
||||
+ (void) _InitROM;
|
||||
+ (BOOL) ModelHasROM:(unsigned)modelCode;
|
||||
+ (BOOL) ScanFileForROM:(NSString*)fpath;
|
||||
+ (void) ScanDirectoryForROM:(NSString*)dpath;
|
||||
- (void) _PrepareModel;
|
||||
|
||||
@end
|
||||
|
||||
@interface A2Computer (UserInterface)
|
||||
|
||||
+ (void) InputPaddlesByKeypad:(char)ch;
|
||||
+ (void) InputPaddlesByMouse;
|
||||
+ (void) SetMouseRangeTo:(NSRect)r;
|
||||
- (BOOL) InputChar:(unichar)ch;
|
||||
- (void) InputChars:(NSString*)str;
|
||||
- (unsigned) ModelCode;
|
||||
- (NSString*) ModelName;
|
||||
- (IBAction) SignalReset:(id)sender;
|
||||
- (IBAction) SignalReboot:(id)sender;
|
||||
- (unsigned) Lights;
|
||||
- (NSString*) TextScreenAsString:(BOOL)newLines;
|
||||
+ (BOOL) ShouldShowDiskFilename:(NSString*)path;
|
||||
+ (void) _UpdateClock:(NSTimer*)timer;
|
||||
|
||||
- (id<A2PrDiskDrive>) DiskDrive:(unsigned)index;
|
||||
|
||||
@end
|
||||
|
||||
@interface A2Computer (Video)
|
||||
|
||||
+ (void) _InitVideo;
|
||||
- (void) RenderScreen:(void*)pixBase:(int32_t)rowBytes;
|
||||
|
||||
@end
|
||||
|
||||
//---------------------------------------------------------------------------
|
1
Source/LibAppleII/myROM.h
Normal file
1
Source/LibAppleII/myROM.h
Normal file
@ -0,0 +1 @@
|
||||
162,192,44,48,192,169,12,32,43,255,202,208,245,169,160,157,0,4,157,0,5,157,0,6,157,0,7,232,208,241,189,55,255,240,254,9,128,157,40,4,232,208,243,56,72,233,1,208,252,104,233,1,208,246,96,78,111,32,82,79,77,32,119,97,115,32,102,111,117,110,100,32,102,111,114,32,116,104,105,115,32,109,111,100,101,108,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,0,255,72,24,165,56,208,8,169,27,133,56,169,253,133,57,165,54,208,9,169,37,133,54,169,193,133,55,56,169,0,133,36,133,33,104,176,1,96,41,127,32,71,193,201,32,144,246,201,127,240,242,230,36,165,36,201,80,144,234,169,0,133,36,169,13,32,71,193,169,10,208,0,141,152,192,96,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,120,40,169,88,208,112,0,24,144,1,96,169,194,141,8,191,169,28,141,7,191,169,76,141,6,191,96,160,3,140,191,192,173,191,192,153,144,191,136,16,247,96,160,21,140,191,192,160,17,173,191,192,9,128,153,0,2,136,16,245,96,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,44,130,192,108,252,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,201,32,169,0,201,3,133,68,133,70,133,71,141,1,8,169,1,133,66,169,8,133,69,44,130,192,32,88,255,186,189,0,1,10,10,10,10,133,67,32,56,196,166,67,76,1,8,188,143,192,162,0,169,0,24,96,165,67,16,4,169,40,56,96,41,112,170,164,66,240,232,136,240,10,136,240,34,136,240,228,169,1,56,96,32,134,196,189,131,192,145,68,200,208,248,230,69,189,131,192,145,68,200,208,248,198,69,169,0,24,96,32,134,196,177,68,157,131,192,200,208,248,230,69,177,68,157,131,192,200,208,248,240,227,160,0,152,157,128,192,165,70,10,157,129,192,165,71,42,157,130,192,96,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,79,56,
|
18
Source/License.html
Normal file
18
Source/License.html
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
|
||||
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
||||
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="application/xhtml+xml;charset=ISO-8859-1" />
|
||||
<title > Catakig: License </title>
|
||||
<style type='text/css'>
|
||||
._ID { padding-left: 3em }
|
||||
._QD { padding-left: 3em; padding-right: 3em }
|
||||
li { margin-bottom: 1ex }
|
||||
body { line-height: 130%; text-align: justify; margin: 1em; background-color: white }
|
||||
</style> </head> <body>
|
||||
<b > <p > Catakig <br clear="all" /> Copyright (c) 1997 Colin Klipsch <br clear="all" /> All rights reserved. </p> </b> <p > Permission is hereby granted, free of charge, to redistribute and use the source code and binary forms with or without modification, provided that the following conditions are met: <ol > <li /> Redistributions of source code must retain the
|
||||
above copyright notice, this list of conditions and the following disclaimer. <li /> Redistributions of binary forms must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. <li /> The name of the copyright holder may not be used to endorse or
|
||||
promote products derived from this software without specific prior written permission. </ol> </p> <small /> <tt > <p > THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. </p> <p > THE USER IS ADVISED TO TEST THIS SOFTWARE THOROUGHLY BEFORE RELYING ON IT, AND MUST ASSUME THE ENTIRE RISK OF USING IT. </p> </tt>
|
||||
</body> </html>
|
9
Source/Misc/IndicatorLight.h
Normal file
9
Source/Misc/IndicatorLight.h
Normal file
@ -0,0 +1,9 @@
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
@interface IndicatorLight : NSImageView
|
||||
{
|
||||
}
|
||||
|
||||
- (void) ToggleState;
|
||||
|
||||
@end
|
60
Source/Misc/IndicatorLight.m
Normal file
60
Source/Misc/IndicatorLight.m
Normal file
@ -0,0 +1,60 @@
|
||||
/* class IndicatorLight
|
||||
|
||||
A view, descended from NSImageView, that displays an "indicator
|
||||
light". The light's visible state is a sub-rectangle of a
|
||||
larger image containing all possible light states. We use
|
||||
NSImageView alignment values to shift among the states.
|
||||
*/
|
||||
#import "IndicatorLight.h"
|
||||
|
||||
@implementation IndicatorLight
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
// Give these attributes sensible values, regardless of how they're
|
||||
// set in the NIB file.
|
||||
|
||||
[self setEditable:NO];
|
||||
[self setEnabled:NO];
|
||||
[self setAlignment:NSImageAlignTopLeft];
|
||||
[self setImageFrameStyle:NSImageFrameNone];
|
||||
[self setImageScaling:NSScaleNone];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (int)intValue
|
||||
{/*
|
||||
Returns this light's current state as a value from 0 to 8.
|
||||
|
||||
For my (CK) reference, NSImageAlign and their state values:
|
||||
2 1 3 0 4 1
|
||||
4 0 8 5 6 7
|
||||
6 5 7 2 8 3
|
||||
*/
|
||||
return "\6\4\0\1\5\x8\2\3\7"[[self imageAlignment]];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)setIntValue:(int)state
|
||||
{/*
|
||||
Sets this light's state to a value from 0 to 8.
|
||||
*/
|
||||
[self setImageAlignment:(int)("\2\3\6\7\1\4\0\x8\5"[state % 9])];
|
||||
// [self setNeedsDisplay];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)ToggleState
|
||||
{/*
|
||||
Toggles this light between on and off (assuming the low bit of the
|
||||
state value reflects this property).
|
||||
*/
|
||||
[self setIntValue:[self intValue] ^ 1];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
50
Source/Misc/MU-Data.m
Normal file
50
Source/Misc/MU-Data.m
Normal file
@ -0,0 +1,50 @@
|
||||
#import "MyUtils.h"
|
||||
#import <bzlib.h>
|
||||
#import <zlib.h>
|
||||
|
||||
@implementation NSData (MyUtils)
|
||||
//---------------------------------------------------------------------------
|
||||
#if 0 //broken!!
|
||||
|
||||
+ (NSData*)GZCompressBytes:(const void*)src length:(unsigned)srcLen
|
||||
level:(int)level
|
||||
{
|
||||
NSMutableData* dest = nil;
|
||||
|
||||
int sts;
|
||||
uLongf destLen = compressBound(srcLen);
|
||||
|
||||
if (nil == (dest = [NSMutableData dataWithLength:destLen]))
|
||||
return nil;
|
||||
|
||||
sts = compress2([dest mutableBytes], &destLen, src, srcLen, level);
|
||||
if (sts != Z_OK)
|
||||
return nil;
|
||||
|
||||
[dest setLength:destLen];
|
||||
return dest;
|
||||
}
|
||||
#endif
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
+ (NSData*)BZCompressBytes:(const void*)src length:(unsigned)srcLen
|
||||
level:(int)level
|
||||
{
|
||||
int sts;
|
||||
unsigned destLen = srcLen + srcLen/100 + 601;
|
||||
NSMutableData* dest = [NSMutableData dataWithLength:destLen];
|
||||
|
||||
if (dest == nil)
|
||||
return nil;
|
||||
|
||||
sts = BZ2_bzBuffToBuffCompress([dest mutableBytes], &destLen,
|
||||
(char*)src, srcLen, level, 0, 0);
|
||||
if (sts != BZ_OK)
|
||||
return nil;
|
||||
|
||||
[dest setLength:destLen];
|
||||
return dest;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
17
Source/Misc/MU-Movie.m
Normal file
17
Source/Misc/MU-Movie.m
Normal file
@ -0,0 +1,17 @@
|
||||
#import "MyUtils.h"
|
||||
|
||||
@implementation NSMovie (MyUtils)
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (id)InitWithResource:(NSString*)fname
|
||||
{
|
||||
NSURL* url = [NSURL fileURLWithPath:[fname PathForResource]];
|
||||
|
||||
if (url == nil)
|
||||
return [self Release];
|
||||
|
||||
return [self initWithURL:url byReference:YES];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
10
Source/Misc/MU-Object.m
Normal file
10
Source/Misc/MU-Object.m
Normal file
@ -0,0 +1,10 @@
|
||||
#import "MyUtils.h"
|
||||
|
||||
@implementation NSObject (MyUtils)
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (id)Release
|
||||
{ [self release]; return nil; }
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
20
Source/Misc/MU-OpenGLContext.m
Normal file
20
Source/Misc/MU-OpenGLContext.m
Normal file
@ -0,0 +1,20 @@
|
||||
#import "MyUtils.h"
|
||||
|
||||
@implementation NSOpenGLContext (MyUtils)
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)SetSwapInterval:(long)interval
|
||||
{ [self setValues:&interval forParameter:NSOpenGLCPSwapInterval]; }
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (NSOpenGLContext*)MakeCurrentContext
|
||||
{
|
||||
NSOpenGLContext* prev = [NSOpenGLContext currentContext];
|
||||
|
||||
[self makeCurrentContext];
|
||||
return prev;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
134
Source/Misc/MU-OpenGLView.m
Normal file
134
Source/Misc/MU-OpenGLView.m
Normal file
@ -0,0 +1,134 @@
|
||||
#import "MyUtils.h"
|
||||
|
||||
@implementation NSOpenGLView (MyUtils)
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)FlushBuffer
|
||||
{ [[self openGLContext] flushBuffer]; }
|
||||
|
||||
- (NSOpenGLContext*)MakeCurrentContext
|
||||
{ return [[self openGLContext] MakeCurrentContext]; }
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static void FlipVertically(NSBitmapImageRep* imRep)
|
||||
{
|
||||
int height = [imRep pixelsHigh];
|
||||
int32_t intsPerRow = [imRep bytesPerRow] / 4;
|
||||
uint32_t *plo = (uint32_t*) [imRep bitmapData],
|
||||
*phi = plo + intsPerRow * (height-1),
|
||||
temp;
|
||||
|
||||
for (int i = height/2; --i >= 0;)
|
||||
{
|
||||
for (int j = intsPerRow; --j >= 0;)
|
||||
temp = plo[j], plo[j] = phi[j], phi[j] = temp;
|
||||
plo += intsPerRow;
|
||||
phi -= intsPerRow;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
static void ResetContext(void)
|
||||
{
|
||||
const GLfloat zero = 0.;
|
||||
|
||||
glDisable(GL_COLOR_TABLE);
|
||||
glDisable(GL_CONVOLUTION_1D);
|
||||
glDisable(GL_CONVOLUTION_2D);
|
||||
glDisable(GL_HISTOGRAM);
|
||||
glDisable(GL_MINMAX);
|
||||
glDisable(GL_POST_COLOR_MATRIX_COLOR_TABLE);
|
||||
glDisable(GL_POST_CONVOLUTION_COLOR_TABLE);
|
||||
glDisable(GL_SEPARABLE_2D);
|
||||
|
||||
glPixelMapfv(GL_PIXEL_MAP_R_TO_R, 1, &zero);
|
||||
glPixelMapfv(GL_PIXEL_MAP_G_TO_G, 1, &zero);
|
||||
glPixelMapfv(GL_PIXEL_MAP_B_TO_B, 1, &zero);
|
||||
glPixelMapfv(GL_PIXEL_MAP_A_TO_A, 1, &zero);
|
||||
|
||||
glPixelStorei(GL_PACK_SWAP_BYTES, 0);
|
||||
glPixelStorei(GL_PACK_LSB_FIRST, 0);
|
||||
glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 4); // or 3??
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||
glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
|
||||
glPixelStorei(GL_PACK_SKIP_ROWS, 0);
|
||||
glPixelStorei(GL_PACK_SKIP_IMAGES, 0);
|
||||
|
||||
glPixelTransferi(GL_MAP_COLOR, 0);
|
||||
glPixelTransferf(GL_RED_SCALE, 1.0f);
|
||||
glPixelTransferf(GL_RED_BIAS, 0.0f);
|
||||
glPixelTransferf(GL_GREEN_SCALE, 1.0f);
|
||||
glPixelTransferf(GL_GREEN_BIAS, 0.0f);
|
||||
glPixelTransferf(GL_BLUE_SCALE, 1.0f);
|
||||
glPixelTransferf(GL_BLUE_BIAS, 0.0f);
|
||||
glPixelTransferf(GL_ALPHA_SCALE, 1.0f);
|
||||
glPixelTransferf(GL_ALPHA_BIAS, 0.0f);
|
||||
glPixelTransferf(GL_POST_COLOR_MATRIX_RED_SCALE, 1.0f);
|
||||
glPixelTransferf(GL_POST_COLOR_MATRIX_RED_BIAS, 0.0f);
|
||||
glPixelTransferf(GL_POST_COLOR_MATRIX_GREEN_SCALE, 1.0f);
|
||||
glPixelTransferf(GL_POST_COLOR_MATRIX_GREEN_BIAS, 0.0f);
|
||||
glPixelTransferf(GL_POST_COLOR_MATRIX_BLUE_SCALE, 1.0f);
|
||||
glPixelTransferf(GL_POST_COLOR_MATRIX_BLUE_BIAS, 0.0f);
|
||||
glPixelTransferf(GL_POST_COLOR_MATRIX_ALPHA_SCALE, 1.0f);
|
||||
glPixelTransferf(GL_POST_COLOR_MATRIX_ALPHA_BIAS, 0.0f);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (NSBitmapImageRep*)ReadPixels
|
||||
{/*
|
||||
Returns an auto-released NSBitmapImageRep containing a snapshot of this
|
||||
OpenGL view. (And that's a lot harder than you'd think.)
|
||||
*/
|
||||
NSSize size = NSIntegralRect([self bounds]).size;
|
||||
NSBitmapImageRep* imRep;
|
||||
|
||||
imRep = [[NSBitmapImageRep alloc]
|
||||
initWithBitmapDataPlanes: nil
|
||||
pixelsWide: size.width
|
||||
pixelsHigh: size.height
|
||||
bitsPerSample: 8
|
||||
samplesPerPixel: 3
|
||||
hasAlpha: NO
|
||||
isPlanar: NO
|
||||
colorSpaceName: NSDeviceRGBColorSpace
|
||||
// NSCalibratedRGBColorSpace??
|
||||
bytesPerRow: 0
|
||||
bitsPerPixel: 0 ];
|
||||
if (imRep == nil)
|
||||
return nil;
|
||||
|
||||
|
||||
NSOpenGLContext* prevContext = [self MakeCurrentContext];
|
||||
|
||||
// glFinish(); // finish any pending OpenGL commands
|
||||
glPushAttrib(GL_ALL_ATTRIB_BITS);
|
||||
ResetContext();
|
||||
// glPixelStorei(GL_PACK_ROW_LENGTH, [imRep bytesPerRow]/kSamples);
|
||||
// glReadBuffer(GL_BACK); // need??
|
||||
glReadPixels(0, 0, size.width, size.height,
|
||||
GL_RGB, GL_UNSIGNED_BYTE, [imRep bitmapData]);
|
||||
glPopAttrib();
|
||||
|
||||
FlipVertically(imRep);
|
||||
[prevContext makeCurrentContext];
|
||||
return [imRep autorelease];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (void)PrepareToMiniaturize
|
||||
{
|
||||
NSBitmapImageRep* image = [self ReadPixels];
|
||||
|
||||
[self lockFocus];
|
||||
[image draw];
|
||||
[self unlockFocus];
|
||||
[[self window] flushWindow]; // need??
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
22
Source/Misc/MU-Panel.m
Normal file
22
Source/Misc/MU-Panel.m
Normal file
@ -0,0 +1,22 @@
|
||||
#import "MyUtils.h"
|
||||
|
||||
@implementation NSPanel (MyUtils)
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (int)RunModal
|
||||
{
|
||||
int response;
|
||||
|
||||
/// [self center]; // always center??
|
||||
response = [NSApp runModalForWindow:self];
|
||||
[self close]; // or call 'orderOut'??
|
||||
return response;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (IBAction)StopModal:(id)sender
|
||||
{ [NSApp stopModalWithCode:[sender tag]]; }
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
31
Source/Misc/MU-Pasteboard.m
Normal file
31
Source/Misc/MU-Pasteboard.m
Normal file
@ -0,0 +1,31 @@
|
||||
#import "MyUtils.h"
|
||||
|
||||
@implementation NSPasteboard (MyUtils)
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (NSString*)GetString
|
||||
{
|
||||
[self types];
|
||||
return [self stringForType:NSStringPboardType];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (BOOL)SetString:(NSString*)str
|
||||
{
|
||||
NSString* type = NSStringPboardType;
|
||||
|
||||
[self declareTypes:[NSArray arrayWithObject:type] owner:nil];
|
||||
return [self setString:str forType:type];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (BOOL)SetData:(NSData*)data forType:(NSString*)type
|
||||
{
|
||||
[self declareTypes:[NSArray arrayWithObject:type] owner:nil];
|
||||
return [self setData:data forType:type];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
20
Source/Misc/MU-Screen.m
Normal file
20
Source/Misc/MU-Screen.m
Normal file
@ -0,0 +1,20 @@
|
||||
#import "MyUtils.h"
|
||||
|
||||
@implementation NSScreen (MyUtils)
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
+ (NSScreen*)MenuBarScreen
|
||||
{ return [[NSScreen screens] objectAtIndex:0]; }
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
#ifdef __CGDIRECT_DISPLAY_H__
|
||||
|
||||
- (CGDirectDisplayID)DirectDisplayID
|
||||
{
|
||||
return [[[self deviceDescription]
|
||||
objectForKey:@"NSScreenNumber"] pointerValue];
|
||||
}
|
||||
|
||||
#endif
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
90
Source/Misc/MU-String.m
Normal file
90
Source/Misc/MU-String.m
Normal file
@ -0,0 +1,90 @@
|
||||
#import "MyUtils.h"
|
||||
#import <regex.h>
|
||||
#import <sys/stat.h>
|
||||
|
||||
@implementation NSString (MyUtils)
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (BOOL)SetFileCreator:(uint32_t)creatorCode andType:(uint32_t)typeCode
|
||||
{/*
|
||||
Sets the HFS creator and type codes for the file given by this string's
|
||||
path. Passing 0 for either parameter indicates no change is to be made
|
||||
for that attribute. Returns whether successful.
|
||||
*/
|
||||
NSFileManager* fileMgr = [NSFileManager defaultManager];
|
||||
NSDictionary* prevAttr;
|
||||
NSMutableDictionary* attr;
|
||||
|
||||
prevAttr = [fileMgr fileAttributesAtPath:self traverseLink:YES];
|
||||
if (prevAttr == nil)
|
||||
return NO;
|
||||
|
||||
attr = [NSMutableDictionary dictionaryWithDictionary:prevAttr];
|
||||
if (creatorCode != 0)
|
||||
[attr setObject:[NSNumber numberWithUnsignedLong:creatorCode]
|
||||
forKey:NSFileHFSCreatorCode];
|
||||
if (typeCode != 0)
|
||||
[attr setObject:[NSNumber numberWithUnsignedLong:typeCode]
|
||||
forKey:NSFileHFSTypeCode];
|
||||
|
||||
return [fileMgr changeFileAttributes:attr atPath:self];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (BOOL)SetFileExtension:(NSString*)extension
|
||||
{/*
|
||||
Renames this file's extension to the given one, or if 'extension' is
|
||||
nil, to an empty extension.
|
||||
*/
|
||||
NSString* newPath;
|
||||
|
||||
newPath = [self stringByDeletingPathExtension];
|
||||
if (extension != nil)
|
||||
newPath = [newPath stringByAppendingPathExtension:extension];
|
||||
|
||||
return [[NSFileManager defaultManager]
|
||||
movePath:self toPath:newPath handler:nil];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (uint32_t)FileTypeCode
|
||||
{/*
|
||||
Returns the HFS type code of this file, or 0 if the type is
|
||||
unavailable.
|
||||
*/
|
||||
NSDictionary* attr;
|
||||
NSNumber* type;
|
||||
|
||||
attr = [[NSFileManager defaultManager]
|
||||
fileAttributesAtPath:self traverseLink:YES];
|
||||
if (attr == nil)
|
||||
return 0;
|
||||
|
||||
type = [attr objectForKey:NSFileHFSTypeCode];
|
||||
return type? [type unsignedLongValue] : 0;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (BOOL)IsReadableFile
|
||||
{ return [[NSFileManager defaultManager] isReadableFileAtPath:self]; }
|
||||
|
||||
- (BOOL)Matches:(const regex_t*)rex
|
||||
{ return 0 == regexec(rex, [self UTF8String], 0, nil, 0); }
|
||||
|
||||
- (BOOL)ExtensionMatches:(const regex_t*)rex
|
||||
{ return [[self pathExtension] Matches:rex]; }
|
||||
|
||||
- (BOOL)Stat:(struct stat*)st
|
||||
{ return 0 == stat([self fileSystemRepresentation], st); }
|
||||
|
||||
- (BOOL)Truncate:(size_t)length
|
||||
{ return 0 == truncate([self fileSystemRepresentation], length); }
|
||||
|
||||
- (NSString*)PathForResource
|
||||
{ return [[NSBundle mainBundle] pathForResource:self ofType:nil]; }
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
35
Source/Misc/MU-UserDefaults.m
Normal file
35
Source/Misc/MU-UserDefaults.m
Normal file
@ -0,0 +1,35 @@
|
||||
#import "MyUtils.h"
|
||||
|
||||
@implementation NSUserDefaults (MyUtils)
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (BOOL)RegisterBool:(BOOL)value forKey:(NSString*)key
|
||||
{
|
||||
[self registerDefaults:[NSDictionary dictionaryWithObject:
|
||||
[NSNumber numberWithBool:value] forKey:key]];
|
||||
|
||||
return [self boolForKey:key];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (int)RegisterInteger:(int)value forKey:(NSString*)key
|
||||
{
|
||||
[self registerDefaults:[NSDictionary dictionaryWithObject:
|
||||
[NSNumber numberWithInt:value] forKey:key]];
|
||||
|
||||
return [self integerForKey:key];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (float)RegisterFloat:(float)value forKey:(NSString*)key
|
||||
{
|
||||
[self registerDefaults:[NSDictionary dictionaryWithObject:
|
||||
[NSNumber numberWithFloat:value] forKey:key]];
|
||||
|
||||
return [self floatForKey:key];
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
13
Source/Misc/MU-View.m
Normal file
13
Source/Misc/MU-View.m
Normal file
@ -0,0 +1,13 @@
|
||||
#import "MyUtils.h"
|
||||
|
||||
@implementation NSView (MyUtils)
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
//+ (void)FillCurrentViewWithColor:(NSColor*)color
|
||||
// { [color set]; [NSBezierPath fillRect:[self bounds]]; }
|
||||
|
||||
- (BOOL)MakeFirstResponder
|
||||
{ return [[self window] makeFirstResponder:self]; }
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
102
Source/Misc/MyUtils.h
Normal file
102
Source/Misc/MyUtils.h
Normal file
@ -0,0 +1,102 @@
|
||||
/* MyUtils.h
|
||||
|
||||
My own (CK's) generally useful additions to the Cocoa/GNUstep classes
|
||||
-- not specific to any particular project.
|
||||
*/
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
#import <sys/types.h>
|
||||
//mport <fcntl.h>
|
||||
#import <stdint.h>
|
||||
#import <unistd.h>
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@interface NSData (MyUtils)
|
||||
|
||||
+ (NSData*) BZCompressBytes:(const void*)src length:(unsigned)srcLen
|
||||
level:(int)level;
|
||||
|
||||
@end
|
||||
//---------------------------------------------------------------------------
|
||||
@interface NSMovie (MyUtils)
|
||||
|
||||
- (id) InitWithResource:(NSString*)fname;
|
||||
|
||||
@end
|
||||
//---------------------------------------------------------------------------
|
||||
@interface NSObject (MyUtils)
|
||||
|
||||
- (id) Release;
|
||||
|
||||
@end
|
||||
//---------------------------------------------------------------------------
|
||||
@interface NSOpenGLContext (MyUtils)
|
||||
|
||||
- (void) SetSwapInterval:(long)interval;
|
||||
- (NSOpenGLContext*) MakeCurrentContext;
|
||||
|
||||
@end
|
||||
//---------------------------------------------------------------------------
|
||||
@interface NSOpenGLView (MyUtils)
|
||||
|
||||
- (NSOpenGLContext*) MakeCurrentContext;
|
||||
- (void) FlushBuffer;
|
||||
- (NSBitmapImageRep*) ReadPixels;
|
||||
- (void) PrepareToMiniaturize;
|
||||
|
||||
@end
|
||||
//---------------------------------------------------------------------------
|
||||
@interface NSPanel (MyUtils)
|
||||
|
||||
- (int) RunModal;
|
||||
- (IBAction) StopModal:(id)sender;
|
||||
|
||||
@end
|
||||
//---------------------------------------------------------------------------
|
||||
@interface NSPasteboard (MyUtils)
|
||||
|
||||
- (NSString*) GetString;
|
||||
- (BOOL) SetString:(NSString*)str;
|
||||
- (BOOL) SetData:(NSData*)data forType:(NSString*)type;
|
||||
|
||||
@end
|
||||
//---------------------------------------------------------------------------
|
||||
@interface NSScreen (MyUtils)
|
||||
|
||||
+ (NSScreen*) MenuBarScreen;
|
||||
|
||||
#ifdef __CGDIRECT_DISPLAY_H__
|
||||
- (CGDirectDisplayID) DirectDisplayID;
|
||||
#endif
|
||||
|
||||
@end
|
||||
//---------------------------------------------------------------------------
|
||||
@interface NSString (MyUtils)
|
||||
|
||||
- (BOOL) IsReadableFile;
|
||||
- (BOOL) Matches:(const regex_t*)rex;
|
||||
- (BOOL) ExtensionMatches:(const regex_t*)rex;
|
||||
- (BOOL) SetFileCreator:(uint32_t)cCode andType:(uint32_t)tCode;
|
||||
- (BOOL) SetFileExtension:(NSString*)ext;
|
||||
- (uint32_t) FileTypeCode;
|
||||
- (BOOL) Stat:(struct stat*)st;
|
||||
- (NSString*) PathForResource;
|
||||
- (BOOL) Truncate:(size_t)length;
|
||||
|
||||
@end
|
||||
//---------------------------------------------------------------------------
|
||||
@interface NSUserDefaults (MyUtils)
|
||||
|
||||
- (BOOL) RegisterBool:(BOOL)value forKey:(NSString*)key;
|
||||
- (int) RegisterInteger:(int)value forKey:(NSString*)key;
|
||||
- (float) RegisterFloat:(float)value forKey:(NSString*)key;
|
||||
|
||||
@end
|
||||
//---------------------------------------------------------------------------
|
||||
@interface NSView (MyUtils)
|
||||
|
||||
- (BOOL) MakeFirstResponder;
|
||||
|
||||
@end
|
||||
//---------------------------------------------------------------------------
|
7
Source/Misc/ShieldWindow.h
Normal file
7
Source/Misc/ShieldWindow.h
Normal file
@ -0,0 +1,7 @@
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
@interface ShieldWindow : NSWindow // or NSPanel??
|
||||
{
|
||||
}
|
||||
|
||||
@end
|
39
Source/Misc/ShieldWindow.m
Normal file
39
Source/Misc/ShieldWindow.m
Normal file
@ -0,0 +1,39 @@
|
||||
#import "ShieldWindow.h"
|
||||
|
||||
@implementation ShieldWindow
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (id)initWithContentRect:(NSRect)contentRect
|
||||
styleMask: (unsigned)styleMask_IGNORED
|
||||
backing: (NSBackingStoreType)backingType
|
||||
defer: (BOOL)flag
|
||||
{
|
||||
self = [super
|
||||
initWithContentRect: contentRect
|
||||
styleMask: NSBorderlessWindowMask
|
||||
backing: backingType
|
||||
defer: flag ];
|
||||
if (self == nil)
|
||||
return nil;
|
||||
|
||||
[self setLevel:CGShieldingWindowLevel()];
|
||||
[self setMovableByWindowBackground:NO];
|
||||
// [self setBackgroundColor:[NSColor blackColor]]; // clearColor??
|
||||
|
||||
[self setReleasedWhenClosed:NO];
|
||||
[self setHasShadow:NO];
|
||||
// [self setHidesOnDeactivate:YES];
|
||||
// [self setAcceptsMouseMovedEvents:YES];
|
||||
// [self setDelegate:self];
|
||||
// [self setOpaque:NO];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
- (BOOL)canBecomeKeyWindow
|
||||
{ return YES; }
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@end
|
93
Source/Prefix.pch
Normal file
93
Source/Prefix.pch
Normal file
@ -0,0 +1,93 @@
|
||||
/* Prefix.pch
|
||||
|
||||
A pre-compiled header implicitly included by all source files of
|
||||
this project.
|
||||
*/
|
||||
|
||||
#ifndef __OBJC__
|
||||
#error This project must be compiled as Objective-C/C++ !
|
||||
#endif
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Platform-dependent includes.
|
||||
|
||||
#if defined(GNUSTEP) // then platform is GNUStep on generic Unix
|
||||
|
||||
// (This port is hardly started yet!!)
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
#import <GL/gl.h>
|
||||
#import <GL/glu.h>
|
||||
#import <GL/glext.h>
|
||||
// #import <GL/glut.h>
|
||||
|
||||
#elif defined(__APPLE__) // then platform is Cocoa on MacOS X
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import <OpenGL/OpenGL.h>
|
||||
#import <OpenGL/gl.h>
|
||||
#import <OpenGL/glu.h>
|
||||
#import <OpenGL/glext.h>
|
||||
// #import <GLUT/glut.h>
|
||||
|
||||
#import <AudioUnit/AudioUnit.h>
|
||||
// #import <AudioToolbox/AudioToolbox.h>
|
||||
#import <CoreAudio/CoreAudio.h>
|
||||
// #import <CoreAudioKit/CoreAudioKit.h>
|
||||
|
||||
// #import <QuickTime/QuickTime.h>
|
||||
// #import <QTKit/QTKit.h>
|
||||
// #import <QuartzCore/QuartzCore.h>
|
||||
|
||||
#if 0
|
||||
#import <Carbon/Carbon.h>
|
||||
#else
|
||||
#import <ApplicationServices/ApplicationServices.h>
|
||||
#import <CoreServices/CoreServices.h>
|
||||
#endif
|
||||
|
||||
#else
|
||||
#error Target platform is neither Cocoa nor GNUStep. Can't continue.
|
||||
|
||||
#endif
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Platform-neutral includes.
|
||||
|
||||
#import <sys/types.h>
|
||||
#import <sys/mman.h>
|
||||
#import <sys/stat.h>
|
||||
//mport <sys/time.h>
|
||||
|
||||
#import <ctype.h>
|
||||
//mport <dirent.h>
|
||||
#import <errno.h>
|
||||
#import <fcntl.h>
|
||||
#import <math.h>
|
||||
#import <pthread.h>
|
||||
#import <regex.h>
|
||||
//mport <stdbool.h>
|
||||
#import <stddef.h>
|
||||
#import <stdint.h>
|
||||
#import <stdio.h>
|
||||
#import <stdlib.h>
|
||||
#import <string.h>
|
||||
#import <time.h>
|
||||
#import <unistd.h>
|
||||
|
||||
//mport <bzlib.h>
|
||||
#import <zlib.h>
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Miscellany.
|
||||
|
||||
#if !defined(__cplusplus) && !defined(and)
|
||||
#define and &&
|
||||
#define or ||
|
||||
#define not !
|
||||
#endif
|
||||
|
||||
//---------------------------------------------------------------------------
|
@ -5,22 +5,25 @@ App_Version = 2.00b2
|
||||
|
||||
PRODUCT_NAME = Catakig
|
||||
WRAPPER_EXTENSION = app
|
||||
//CURRENT_PROJECT_VERSION = x.x.x
|
||||
|
||||
//ALWAYS_SEARCH_USER_PATHS = NO
|
||||
// Apple recommends this setting
|
||||
|
||||
INFOPLIST_FILE = Info.plist
|
||||
GCC_PREFIX_HEADER = Source/Prefix.pch
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES
|
||||
GCC_PREFIX_HEADER = Source/Prefix.pch
|
||||
GCC_PFE_FILE_C_DIALECTS = c objective-c
|
||||
PREBINDING = NO
|
||||
|
||||
GCC_INPUT_FILETYPE = sourcecode.c.objc
|
||||
//GCC_INPUT_FILETYPE = automatic
|
||||
// "Compile Sources as"
|
||||
// "Compile Sources as": automatic, sourcecode.c.objc
|
||||
|
||||
GCC_C_LANGUAGE_STANDARD = c99
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES
|
||||
GCC_STRICT_ALIASING = YES
|
||||
//GCC_STRICT_ALIASING = YES
|
||||
|
||||
//GCC_VERSION = 4.0
|
||||
GCC_VERSION_ppc = 4.0 // 3.3
|
||||
GCC_VERSION_ppc = 4.0 // 3.3?
|
||||
GCC_VERSION_i386 = 4.0
|
||||
|
||||
//SDKROOT = /Developer/SDKs/MacOSX10.3.9.sdk
|
||||
@ -40,8 +43,7 @@ OTHER_LDFLAGS = -lz -lbz2
|
||||
|
||||
// ==== Warnings ====
|
||||
|
||||
GCC_WARN_UNUSED_VARIABLE = NO
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES
|
||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES
|
||||
//GCC_WARN_SIGN_COMPARE = YES
|
||||
|
||||
GCC_WARN_UNUSED_VARIABLE = NO
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES
|
||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES
|
||||
//GCC_WARN_SIGN_COMPARE = YES
|
||||
|
@ -1,11 +1,11 @@
|
||||
#include "common" // file 'common.xcconfig'
|
||||
|
||||
ARCHS = ppc i386
|
||||
|
||||
GCC_OPTIMIZATION_LEVEL = 3
|
||||
GCC_DYNAMIC_NO_PIC = YES
|
||||
GCC_GENERATE_DEBUGGING_SYMBOLS = NO
|
||||
|
||||
ARCHS = ppc i386
|
||||
|
||||
SEPARATE_STRIP = YES
|
||||
DEAD_CODE_STRIPPING = YES
|
||||
PRESERVE_DEAD_CODE_INITS_AND_TERMS = YES
|
||||
|
Loading…
Reference in New Issue
Block a user