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">
<dict>
<key>IBDocumentLocation</key>
<string>64 24 356 240 0 0 1152 842 </string>
<string>65 32 356 240 0 0 1152 842 </string>
<key>IBEditorPositions</key>
<dict>
<key>43</key>
@ -16,6 +16,6 @@
<key>IBOldestOS</key>
<integer>3</integer>
<key>IBSystem Version</key>
<string>8J135</string>
<string>8L127</string>
</dict>
</plist>

Binary file not shown.

View File

@ -6,6 +6,30 @@
<string>English</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>dsk</string>
<string>do</string>
<string>po</string>
<string>nyb</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>FloppyDisk</string>
<key>CFBundleTypeName</key>
<string>5-inch disk</string>
<key>CFBundleTypeOSTypes</key>
<array>
<string>A2D5</string>
<string>DSK5</string>
</array>
<key>CFBundleTypeRole</key>
<string>None</string>
<key>LSTypeIsPackage</key>
<false/>
<key>NSPersistentStoreTypeKey</key>
<string>Binary</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>

View File

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

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