For v2.00b2 release.

This commit is contained in:
Colin Klipsch 2006-10-17 22:05:05 +00:00
parent 1d3ce5eeb0
commit 1195235253
63 changed files with 7107 additions and 17 deletions

View File

@ -3,7 +3,7 @@
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>IBDocumentLocation</key> <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> <key>IBEditorPositions</key>
<dict> <dict>
<key>43</key> <key>43</key>
@ -16,6 +16,6 @@
<key>IBOldestOS</key> <key>IBOldestOS</key>
<integer>3</integer> <integer>3</integer>
<key>IBSystem Version</key> <key>IBSystem Version</key>
<string>8J135</string> <string>8L127</string>
</dict> </dict>
</plist> </plist>

Binary file not shown.

View File

@ -6,6 +6,30 @@
<string>English</string> <string>English</string>
<key>CFBundleDocumentTypes</key> <key>CFBundleDocumentTypes</key>
<array> <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> <dict>
<key>CFBundleTypeExtensions</key> <key>CFBundleTypeExtensions</key>
<array> <array>

View File

@ -31,7 +31,13 @@
/TurnOn { Style or /Style exch def} def % nFlags -- /TurnOn { Style or /Style exch def} def % nFlags --
/SpcWidth {(-- ) BlessString stringwidth pop mul} def % nSpaces -- width /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 -- /G0 { 60 G} def % sData --
/G1 {120 G} def % sData -- /G1 {120 G} def % sData --
/G2 {120 G} def % sData -- /G2 {120 G} def % sData --
@ -153,7 +159,6 @@
/setdistillerparams where { pop << /setdistillerparams where { pop <<
%/CompatibilityLevel 1.4 %/CompatibilityLevel 1.4
%/PDFSETTINGS /printer
%/NeverEmbed [/Courier] %/NeverEmbed [/Courier]
%/EmbedAllFonts true %/EmbedAllFonts true
/SubsetFonts true /SubsetFonts true
@ -167,3 +172,4 @@
/Ibsen findfont /T0Font get 12 Scale mul scalefont setfont /Ibsen findfont /T0Font get 12 Scale mul scalefont setfont
Reset Reset

110
Notes/Read-me-first.html Normal file
View 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
View 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
View 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
View 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.)

View File

@ -0,0 +1,9 @@
@interface AboutPanel : NSPanel
{
IBOutlet NSControl* mVersion;
IBOutlet NSTextField* mCursorCover;
NSTimer* mStrober;
}
@end

76
Source/Cocoa/AboutPanel.m Normal file
View 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

View 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

View 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

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

View File

@ -0,0 +1,6 @@
//#import <Cocoa/Cocoa.h>
@interface MyApplication : NSApplication
{
}
@end

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

View 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

View 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

View 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
View 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
View 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
View 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
View 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 receivers 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 supers
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 receivers 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
View 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]);
}
}
//---------------------------------------------------------------------------

View 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

View 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

View 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);
}
}

View 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!!

View 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

View 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

View 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

View 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

View 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

View 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

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

View 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

View 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

View 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

File diff suppressed because one or more lines are too long

307
Source/LibAppleII/Globals.l Normal file
View 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
}
//---------------------------------------------------------------------------

View 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
//---------------------------------------------------------------------------

View 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
//---------------------------------------------------------------------------

View 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
View 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 &#8220;AS IS&#8221;, 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>

View File

@ -0,0 +1,9 @@
#import <AppKit/AppKit.h>
@interface IndicatorLight : NSImageView
{
}
- (void) ToggleState;
@end

View 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
View 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
View 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
View File

@ -0,0 +1,10 @@
#import "MyUtils.h"
@implementation NSObject (MyUtils)
//---------------------------------------------------------------------------
- (id)Release
{ [self release]; return nil; }
//---------------------------------------------------------------------------
@end

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

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

View 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
View 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
View 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
//---------------------------------------------------------------------------

View File

@ -0,0 +1,7 @@
#import <AppKit/AppKit.h>
@interface ShieldWindow : NSWindow // or NSPanel??
{
}
@end

View 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
View 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
//---------------------------------------------------------------------------

View File

@ -5,22 +5,25 @@ App_Version = 2.00b2
PRODUCT_NAME = Catakig PRODUCT_NAME = Catakig
WRAPPER_EXTENSION = app WRAPPER_EXTENSION = app
//CURRENT_PROJECT_VERSION = x.x.x
//ALWAYS_SEARCH_USER_PATHS = NO
// Apple recommends this setting
INFOPLIST_FILE = Info.plist INFOPLIST_FILE = Info.plist
GCC_PREFIX_HEADER = Source/Prefix.pch
GCC_PRECOMPILE_PREFIX_HEADER = YES GCC_PRECOMPILE_PREFIX_HEADER = YES
GCC_PREFIX_HEADER = Source/Prefix.pch
GCC_PFE_FILE_C_DIALECTS = c objective-c GCC_PFE_FILE_C_DIALECTS = c objective-c
PREBINDING = NO
GCC_INPUT_FILETYPE = sourcecode.c.objc GCC_INPUT_FILETYPE = sourcecode.c.objc
//GCC_INPUT_FILETYPE = automatic // "Compile Sources as": automatic, sourcecode.c.objc
// "Compile Sources as"
GCC_C_LANGUAGE_STANDARD = c99 GCC_C_LANGUAGE_STANDARD = c99
GCC_ENABLE_OBJC_EXCEPTIONS = YES GCC_ENABLE_OBJC_EXCEPTIONS = YES
GCC_STRICT_ALIASING = YES //GCC_STRICT_ALIASING = YES
//GCC_VERSION = 4.0 //GCC_VERSION = 4.0
GCC_VERSION_ppc = 4.0 // 3.3 GCC_VERSION_ppc = 4.0 // 3.3?
GCC_VERSION_i386 = 4.0 GCC_VERSION_i386 = 4.0
//SDKROOT = /Developer/SDKs/MacOSX10.3.9.sdk //SDKROOT = /Developer/SDKs/MacOSX10.3.9.sdk
@ -40,8 +43,7 @@ OTHER_LDFLAGS = -lz -lbz2
// ==== Warnings ==== // ==== Warnings ====
GCC_WARN_UNUSED_VARIABLE = NO GCC_WARN_UNUSED_VARIABLE = NO
GCC_WARN_ABOUT_RETURN_TYPE = YES GCC_WARN_ABOUT_RETURN_TYPE = YES
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES
//GCC_WARN_SIGN_COMPARE = YES //GCC_WARN_SIGN_COMPARE = YES

View File

@ -1,11 +1,11 @@
#include "common" // file 'common.xcconfig' #include "common" // file 'common.xcconfig'
ARCHS = ppc i386
GCC_OPTIMIZATION_LEVEL = 3 GCC_OPTIMIZATION_LEVEL = 3
GCC_DYNAMIC_NO_PIC = YES GCC_DYNAMIC_NO_PIC = YES
GCC_GENERATE_DEBUGGING_SYMBOLS = NO GCC_GENERATE_DEBUGGING_SYMBOLS = NO
ARCHS = ppc i386
SEPARATE_STRIP = YES SEPARATE_STRIP = YES
DEAD_CODE_STRIPPING = YES DEAD_CODE_STRIPPING = YES
PRESERVE_DEAD_CODE_INITS_AND_TERMS = YES PRESERVE_DEAD_CODE_INITS_AND_TERMS = YES