Commits

Jonathan Xavier  committed c6c446a

Imported from SVN by Bitbucket

  • Participants

Comments (0)

Files changed (122)

File CalcBackend.h

+//
+//  CalcBackend.h
+//  emu48
+//
+//  Created by Da Woon Jung on 2009-01-23
+//  Copyright 2009 dwj. All rights reserved.
+//
+
+#import "kmlparser.h"
+
+@class CalcEngine;
+@class CalcTimer;
+@class CalcState;
+@class CalcView;
+@class CalcDebugger;
+@class CalcToneGenerator;
+
+
+@interface CalcBackend : NSObject
+{
+    CalcView   *calcView;
+    CalcEngine *engine;
+    CalcTimer  *timer;
+    CalcDebugger *debugModel;
+    CalcToneGenerator *toneGenerator;
+
+    CalcState *state;
+    NSMutableArray *backups;
+//    KmlParseResult *kml;
+    // Keep pointers to avoid calling accessors every button/keypress
+    KmlBlock **pVKey;
+    KmlButton *pButton;
+    unsigned nButtons;
+    DWORD    nKMLFlags;
+    unsigned char byVKeyMap[256];
+    BOOL bClicking;
+    BOOL bPressed;
+    UINT uButtonClicked;
+    UINT uLastPressedKey;
+    KmlButton *drawingButton;
+
+    BOOL initDone;
+    BOOL isRunning;
+}
++ (CalcBackend *)sharedBackend;
+- (BOOL)makeUntitledCalcWithKml:(NSString *)aFilename error:(NSError **)outError;
+- (void)changeKml:(id)sender;
+- (void)run;
+- (void)stop;
+- (BOOL)isRunning;
+- (NSString *)currentModel;
+
+- (CalcTimer *)timer;
+- (CalcView *)calcView;
+- (void)setCalcView:(CalcView *)aView;
+- (CalcDebugger *)debugModel;
+- (void)playToneWithFrequency:(DWORD)freq duration:(DWORD)duration;
+#if TARGET_OS_IPHONE
+- (void)interruptToneWithState:(UInt32)aInterruptState;
+#endif
+
+- (KmlLine *)If:(KmlLine *)pLine condition:(BOOL)bCondition;
+- (KmlLine *)RunLine:(KmlLine *)pLine;
+
+- (void)mouseDownAt:(CalcPoint)aPoint;
+- (void)rightMouseDownAt:(CalcPoint)aPoint;
+- (void)mouseUpAt:(CalcPoint)aPoint;
+- (void)runKey:(BYTE)nId pressed:(BOOL)aPressed;
+
+- (BOOL)ClipButton:(CalcPoint)aPoint forId:(unsigned)nId;
+- (void)DrawButton:(unsigned)nId;
+- (void)PressButton:(unsigned)nId;
+- (void)ReleaseButton:(unsigned)nId;
+- (void)PressButtonById:(unsigned)nId;
+- (void)ReleaseButtonById:(unsigned)nId;
+- (void)ReleaseAllButtons;
+- (BOOL)drawingButtonPressed;
+- (UINT)drawingButtonType;
+- (CalcRect)drawingButtonRect;
+- (CalcRect)drawingButtonRectPressed;
+
+- (void)onPowerKey;
+
+- (BOOL)initDone;
+- (void)setInitDone:(BOOL)aDone;
+
+- (void)finishInitWithViewContainer:(CalcViewContainer *)aViewContainer
+                           lcdClass:(Class)aLcdClass;
+
+- (BOOL)readFromState:(NSString *)aStateFile error:(NSError **)outError;
+- (BOOL)saveStateAs:(NSString *)aStateFile error:(NSError **)outError;
+
+- (BOOL)readFromObject:(NSString *)aObjectFile error:(NSError **)outError;
+- (BOOL)saveObjectAs:(NSString *)aObjectFile error:(NSError **)outError;
+
+- (void)backup;
+- (void)restore;
+@end

File CalcBackend.m

+//
+//  CalcBackend.m
+//  emu48
+//
+//  Created by Da Woon Jung on 2009-01-23
+//  Copyright 2009 dwj. All rights reserved.
+//
+
+#import "CalcBackend.h"
+#import "engine.h"
+#import "timer.h"
+#import "files.h"
+#import "stack.h"
+#import "external.h"
+#import "CalcView.h"
+#import "lcd.h"
+#import "CalcDebugger.h"
+#import "EMU48.H"
+#import "IO.H"
+#import <sys/stat.h>
+#import <sys/mman.h>
+#if TARGET_OS_IPHONE
+#import <AudioToolbox/AudioToolbox.h>
+#endif
+
+
+CalcBackend *gSharedCalcBackend = nil;
+
+@interface CalcBackend(Private)
+- (void)loadEngine;
+- (void)unloadEngine;
+@end
+
+
+@implementation CalcBackend
+
++ (CalcBackend *)sharedBackend
+{
+    if (nil == gSharedCalcBackend)
+        gSharedCalcBackend = [[CalcBackend alloc] init];
+    return gSharedCalcBackend;
+}
+
+- (void)dealloc
+{
+    [self stop];
+    [state release];
+    [backups release];
+    [super dealloc];
+}
+
+- (void)loadEngine
+{
+    if (nil == toneGenerator)
+        toneGenerator = [[CalcToneGenerator alloc] init];
+    if (nil == debugModel)
+        debugModel = [[CalcDebugger alloc] init];
+    if (nil == timer)
+        timer  = [[CalcTimer alloc] init];
+    if (nil == engine)
+        engine = [[CalcEngine alloc] init];
+}
+- (void)unloadEngine
+{
+    [engine release]; engine = nil;
+    [timer release];  timer = nil;
+    [debugModel release]; debugModel = nil;
+    [toneGenerator release]; toneGenerator = nil;
+}
+
+- (BOOL)makeUntitledCalcWithKml:(NSString *)aFilename error:(NSError **)outError
+{
+    [self loadEngine];
+    
+    CalcState *freshState = [[CalcState alloc] initWithKml:aFilename error:outError];
+    if (freshState)
+    {
+        [state release];
+        state = freshState;
+        return YES;
+    }
+    else
+    {
+        [self unloadEngine];
+    }
+    return NO;
+}
+
+- (void)changeKml:(id)sender
+{
+    id path = nil;
+    if ([sender respondsToSelector: @selector(representedObject)])
+        path = [sender representedObject];
+    if (path)
+    {
+        NSArray *pathComps = [[path stringByDeletingLastPathComponent] pathComponents];
+        if ([pathComps count] < 2)
+            path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent: path];
+        NSError *err = nil;
+        [state setKmlFile:path error:&err];
+    }
+}
+
+- (void)run
+{
+    if (engine)
+    {
+        QueryPerformanceFrequency(&lFreq);		// init high resolution counter
+        QueryPerformanceCounter(&lAppStart);
+        SetSpeed(NO);
+        nState     = SM_RUN;					// init state must be <> nNextState
+        nNextState = SM_INVALID;				// go into invalid state
+
+        [NSThread detachNewThreadSelector:@selector(main) toTarget:engine withObject:nil];
+        while (nState!=nNextState) Sleep(0);	// wait for thread initialized
+
+        if (pbyRom)
+        {
+            SwitchToState(SM_RUN);
+            isRunning = YES;
+        }
+    }
+}
+
+- (void)stop
+{
+    [state release]; state = nil;
+    [self unloadEngine];
+    isRunning = NO;
+}
+
+- (BOOL)isRunning
+{
+    return isRunning;
+}
+
+- (NSString *)currentModel
+{
+    return [NSString stringWithFormat:@"%c", cCurrentRomType];
+}
+
+- (CalcTimer *)timer
+{
+    return timer;
+}
+
+- (CalcView *)calcView
+{
+    if ([self initDone])
+    {
+        return calcView;
+    }
+    return nil;
+}
+
+- (void)setCalcView:(CalcView *)aView
+{
+    calcView = aView;
+}
+
+- (CalcDebugger *)debugModel
+{
+    return debugModel;
+}
+
+- (void)playToneWithFrequency:(DWORD)freq duration:(DWORD)duration
+{
+    [toneGenerator playToneWithFrequency:freq duration:duration];
+}
+
+#if TARGET_OS_IPHONE
+- (void)interruptToneWithState:(UInt32)aInterruptState
+{
+    switch (aInterruptState)
+    {
+        case kAudioSessionBeginInterruption:
+            [toneGenerator release]; toneGenerator = nil;
+            break;
+        case kAudioSessionEndInterruption:
+            if (nil == toneGenerator)
+                toneGenerator = [[CalcToneGenerator alloc] init];
+            break;
+        default:
+            break;
+    }
+}
+#endif
+
+- (KmlLine *)If:(KmlLine *)pLine
+      condition:(BOOL)bCondition
+{
+	pLine = pLine->pNext;
+	if (bCondition)
+	{
+		while (pLine)
+		{
+			if (pLine->eCommand == TOK_END)
+			{
+				pLine = pLine->pNext;
+				break;
+			}
+			if (pLine->eCommand == TOK_ELSE)
+			{
+				pLine = SkipLines(pLine, TOK_END);
+				break;
+			}
+			pLine = [self RunLine:pLine];
+		}
+	}
+	else
+	{
+		pLine = SkipLines(pLine, TOK_ELSE);
+		while (pLine)
+		{
+			if (pLine->eCommand == TOK_END)
+			{
+				pLine = pLine->pNext;
+				break;
+			}
+			pLine = [self RunLine:pLine];
+		}
+	}
+	return pLine;
+}
+
+- (KmlLine *)RunLine:(KmlLine *)pLine
+{
+	switch (pLine->eCommand)
+	{
+        case TOK_MAP:
+            if (byVKeyMap[pLine->nParam[0]&0xFF]&1)
+                [self PressButtonById: pLine->nParam[1]];
+            else
+                [self ReleaseButtonById: pLine->nParam[1]];
+            break;
+        case TOK_PRESS:
+            [self PressButtonById: pLine->nParam[0]];
+            break;
+        case TOK_RELEASE:
+            [self ReleaseButtonById: pLine->nParam[0]];
+            break;
+//	case TOK_MENUITEM:
+//		PostMessage(hWnd, WM_COMMAND, 0x19C40+(pLine->nParam[0]&0xFF), 0);
+//		break;
+        case TOK_SETFLAG:
+            nKMLFlags |= 1<<(pLine->nParam[0]&0x1F);
+            break;
+        case TOK_RESETFLAG:
+            nKMLFlags &= ~(1<<(pLine->nParam[0]&0x1F));
+            break;
+        case TOK_NOTFLAG:
+            nKMLFlags ^= 1<<(pLine->nParam[0]&0x1F);
+            break;
+        case TOK_IFPRESSED:
+            return [self If:pLine condition:byVKeyMap[pLine->nParam[0]&0xFF]];
+            break;
+        case TOK_IFFLAG:
+            return [self If:pLine condition:((nKMLFlags>>(pLine->nParam[0]&0x1F))&1)];
+        default:
+            break;
+	}
+	return pLine->pNext;
+}
+
+
+- (void)mouseDownAt:(CalcPoint)aPoint
+{
+	UINT i;
+	for (i=0; i<nButtons; i++)
+	{
+		if ([self ClipButton:aPoint forId:i])
+		{
+			if (pButton[i].dwFlags&BUTTON_NOHOLD)
+			{
+                bClicking = TRUE;
+                uButtonClicked = i;
+                pButton[i].bDown = TRUE;
+                [self DrawButton: i];
+                return;
+			}
+			if (pButton[i].dwFlags&BUTTON_VIRTUAL)
+			{
+				bClicking = TRUE;
+				uButtonClicked = i;
+			}
+			bPressed = TRUE;				// key pressed
+			uLastPressedKey = i;			// save pressed key
+			[self PressButton: i];
+			return;
+		}
+	}
+}
+
+- (void)rightMouseDownAt:(CalcPoint)aPoint
+{
+	UINT i;
+	for (i=0; i<nButtons; i++)
+	{
+		if ([self ClipButton:aPoint forId:i])
+		{
+			if (pButton[i].dwFlags&BUTTON_NOHOLD)
+			{
+                return;
+			}
+			if (pButton[i].dwFlags&BUTTON_VIRTUAL)
+			{
+				return;
+			}
+			bPressed = TRUE;				// key pressed
+			uLastPressedKey = i;			// save pressed key
+			[self PressButton: i];
+			return;
+		}
+	}
+}
+
+- (void)mouseUpAt:(CalcPoint)aPoint
+{
+	UINT i;
+	if (bPressed)							// emulator key pressed
+	{
+		[self ReleaseAllButtons];
+        return;
+	}
+	for (i=0; i<nButtons; i++)
+	{
+		if ([self ClipButton:aPoint forId:i])
+		{
+			if ((bClicking)&&(uButtonClicked != i)) break;
+			[self ReleaseButton :i];
+			break;
+		}
+	}
+	bClicking = FALSE;
+	uButtonClicked = 0;
+}
+
+- (void)runKey:(BYTE)nId pressed:(BOOL)aPressed
+{
+	if (pVKey[nId])
+	{
+		KmlLine *line = pVKey[nId]->pFirstLine;
+		byVKeyMap[nId] = aPressed;
+		while (line) line = [self RunLine: line];
+	}
+	else
+	{
+		if ([[state kml] debug]&&aPressed)
+		{
+			NSString *msgStr = [NSString stringWithFormat: NSLocalizedString(@"Scancode %i",@""), nId];
+			InfoMessage([msgStr UTF8String]);
+		}
+	}
+}
+
+
+- (BOOL)ClipButton:(CalcPoint)aPoint forId:(unsigned)nId
+{
+	return (pButton[nId].nOx<=aPoint.x)
+        && (pButton[nId].nOy<=aPoint.y)
+        && (aPoint.x<(pButton[nId].nOx+pButton[nId].nCx))
+        && (aPoint.y<(pButton[nId].nOy+pButton[nId].nCy));
+}
+
+- (void)DrawButton:(unsigned)nId
+{
+    drawingButton = &pButton[nId];
+    [calcView buttonDrawing];
+}
+
+- (BOOL)drawingButtonPressed
+{
+    return drawingButton ? drawingButton->bDown : NO;
+}
+
+- (UINT)drawingButtonType
+{
+    return drawingButton ? drawingButton->nType : 0;
+}
+
+- (CalcRect)drawingButtonRect
+{
+    CalcRect result = CalcZeroRect;
+    if (drawingButton)
+        result = CalcMakeRect(drawingButton->nOx, drawingButton->nOy, drawingButton->nCx, drawingButton->nCy);
+    return result;
+}
+
+- (CalcRect)drawingButtonRectPressed
+{
+    CalcRect result = CalcZeroRect;
+    if (drawingButton)
+        result = CalcMakeRect(drawingButton->nDx, drawingButton->nDy, drawingButton->nCx, drawingButton->nCy);
+    return result;
+}
+
+- (void)PressButton:(unsigned)nId
+{
+	if (pButton[nId].bDown) return;			// key already pressed -> exit
+    
+	pButton[nId].bDown = TRUE;
+	[self DrawButton: nId];
+	if (pButton[nId].nIn)
+	{
+		KeyboardEvent(TRUE,pButton[nId].nOut,pButton[nId].nIn);
+	}
+	else
+	{
+		KmlLine* pLine = pButton[nId].pOnDown;
+		while ((pLine)&&(pLine->eCommand!=TOK_END))
+		{
+			pLine = [self RunLine: pLine];
+		}
+	}
+}
+
+- (void)ReleaseButton:(unsigned)nId
+{
+	pButton[nId].bDown = FALSE;
+	[self DrawButton: nId];
+	if (pButton[nId].nIn)
+	{
+		KeyboardEvent(FALSE,pButton[nId].nOut,pButton[nId].nIn);
+	}
+	else
+	{
+		KmlLine* pLine = pButton[nId].pOnUp;
+		while ((pLine)&&(pLine->eCommand!=TOK_END))
+		{
+			pLine = [self RunLine: pLine];
+		}
+	}
+}
+
+- (void)PressButtonById:(unsigned)nId
+{
+	UINT i;
+	for (i=0; i<nButtons; i++)
+	{
+		if (nId == pButton[i].nId)
+		{
+			[self PressButton: i];
+			return;
+		}
+	}
+}
+
+- (void)ReleaseButtonById:(unsigned)nId
+{
+	UINT i;
+	for (i=0; i<nButtons; i++)
+	{
+		if (nId == pButton[i].nId)
+		{
+			[self ReleaseButton: i];
+			return;
+		}
+	}
+}
+
+- (void)ReleaseAllButtons
+{
+	UINT i;
+	for (i=0; i<nButtons; i++)				// scan all buttons
+	{
+		if (pButton[i].bDown)				// button pressed
+			[self ReleaseButton: i];		// release button
+	}
+    
+	bPressed = FALSE;						// key not pressed
+	bClicking = FALSE;						// var uButtonClicked not valid (no virtual or nohold key)
+	uButtonClicked = 0;						// set var to default
+}
+
+
+- (void)onPowerKey
+{
+    KeyboardEvent(TRUE,0,0x8000);
+    Sleep(200);
+    KeyboardEvent(FALSE,0,0x8000);
+    Sleep(200);
+}
+
+- (BOOL)initDone
+{
+    return initDone;
+}
+
+- (void)setInitDone:(BOOL)value
+{
+    initDone = value;
+}
+
+
+- (void)finishInitWithViewContainer:(CalcViewContainer *)aViewContainer
+                           lcdClass:(Class)aLcdClass
+{
+    KmlParseResult *kml = [state kml];
+    pVKey     = [kml VKeys];
+    pButton   = [kml buttons];
+    nButtons  = [kml countOfButtons];
+
+    CalcRect bg = [kml background];
+    if (bg.size.width>0.f && bg.size.height>0.f &&
+        [aViewContainer respondsToSelector:@selector(setContentSize:)])
+    {
+        [aViewContainer setContentSize: bg.size];
+    }
+    [calcView setMainBitmap:[kml mainBitmap] atOrigin:bg.origin];
+
+    KmlAnnunciatorC *pAnnunciator = [kml annunciators];
+    int i;
+    for (i = 0; i < 6; ++i)
+    {
+        // position of annunciator
+        CalcRect annunRect = CalcMakeRect(pAnnunciator[i].nDx, pAnnunciator[i].nDy, pAnnunciator[i].nCx, pAnnunciator[i].nCy);
+        [calcView setAnnunciatorRect:annunRect atIndex:i isOn:YES];
+        // position of background
+        annunRect.origin.x = pAnnunciator[i].nOx;
+        annunRect.origin.y = pAnnunciator[i].nOy;
+        [calcView setAnnunciatorRect:annunRect atIndex:i isOn:NO];
+    }
+
+    [calcView setLCD:[[[aLcdClass alloc] initWithScale:[kml lcdScale] colors:[kml lcdColors]] autorelease] atOrigin:[kml lcdOrigin]];
+    [calcView setLcdGrayscaleMode: [[NSUserDefaults standardUserDefaults] boolForKey: @"Grayscale"]];
+#if TARGET_OS_IPHONE
+    [calcView setNeedsDisplay];
+#else
+    [calcView setNeedsDisplay: YES];
+#endif
+
+    [self setInitDone: YES];
+}
+
+#pragma mark -
+#pragma mark Open/Save state
+
+- (BOOL)readFromState:(NSString *)statePath error:(NSError **)outError
+{
+    [self loadEngine];
+    CalcState *freshState = [[CalcState alloc] initWithFile:statePath error:outError];
+    if (freshState)
+    {
+        [state release];
+        state = freshState;
+        return YES;
+    }
+    else
+    {
+        [self unloadEngine];
+    }
+    return NO;
+}
+
+- (BOOL)saveStateAs:(NSString *)aStateFile error:(NSError **)outError
+{
+    return [state saveAs:aStateFile error:outError];
+}
+
+#pragma mark -
+#pragma mark Import/Export object
+
+- (BOOL)readFromObject:(NSString *)aObjectFile error:(NSError **)outError
+{
+    NSData *data = [[NSData alloc] initWithContentsOfFile:aObjectFile options:(NSMappedRead | NSUncachedRead) error:outError];
+    CalcStack *stack = nil;
+    if (data)
+    {
+        stack = [[CalcStack alloc] initWithObject: data];
+        [stack pasteObjectRepresentation: outError];
+        [stack release];
+        [data release];
+        return (nil == outError);
+    }
+    return NO;
+}
+
+- (BOOL)saveObjectAs:(NSString *)aObjectFile error:(NSError **)outError
+{
+    BOOL result = NO;
+    CalcStack *stack = [[CalcStack alloc] initWithError: outError];
+    if (stack)
+    {
+        NSData *object = [stack objectRepresentation];
+        result = [object writeToFile:aObjectFile options:NSAtomicWrite error:outError];
+        [stack release];
+    }
+    return result;
+}
+
+#pragma mark -
+#pragma mark Backup/Restore
+
+- (void)backup
+{
+	UINT nOldState;
+	if (pbyRom == NULL) return;
+	nOldState = SwitchToState(SM_INVALID);
+    if (nil == backups) backups = [[NSMutableArray alloc] init];
+    // TODO: Maybe implement multiple backups?
+    [backups removeAllObjects];
+    NSDictionary *backup = [[NSDictionary alloc] initWithObjectsAndKeys:
+                            [NSDate date], @"date",
+                            [[[CalcBackup alloc] initWithState: state] autorelease], @"state",
+                            nil];
+    [backups addObject: backup];
+    [backup release];
+	SwitchToState(nOldState);
+}
+
+- (void)restore
+{
+	SwitchToState(SM_INVALID);
+    if (backups && [backups count] > 0)
+    {
+        NSDictionary *backup = [backups objectAtIndex: 0];
+        [[backup objectForKey: @"state"] restoreToState: state];
+    }
+	if (pbyRom) SwitchToState(SM_RUN);
+}
+@end

File CalcDebugger.h

+//
+//  CalcDebugger.h
+//  emu48
+//
+//  Created by Da Woon Jung on Thu Feb 19 2004.
+//  Copyright (c) 2004 dwj. All rights reserved.
+//
+
+#import "pch.h"
+#import "EMU48.H"
+
+@class CalcCheckBreakpointArgument;
+@class CalcBreakpoint;
+
+
+@interface CalcDebugger : NSObject
+{
+    CHIPSET OldChipset;
+    DWORD dbgRefCycles;
+    DWORD dbgRstkp;
+    INT breakType;
+    INT dbgOldState;
+    BOOL breakpointsEnabled;
+    DWORD disassemblyStartAddress;
+    NSMutableArray *registers;
+    NSMutableArray *memory;
+    NSMutableArray *stack;
+    NSMutableArray *breakpoints;
+    NSMutableArray *history;
+    NSDictionary   *profile;
+    NSDictionary   *woRegisters;
+    NSMutableDictionary *mmu;
+    NSMutableDictionary *misc;
+    NSMutableDictionary *regUpdated;
+}
+- (NSMutableArray *)disassembly;
+- (void)setDisassembly:(NSMutableArray *)dummy;
+- (void)setDisassemblyStartAddress:(DWORD)aDisassemblyStartAddress;
+- (NSMutableArray *)registers;
+- (void)setRegisters:(NSMutableArray *)aRegisters;
+- (NSMutableArray *)memory;
+- (void)setMemory:(NSMutableArray *)aMemory;
+- (NSMutableArray *)stack;
+- (void)setStack:(NSMutableArray *)aStack;
+- (NSMutableArray *)breakpoints;
+- (void)setBreakpoints:(NSMutableArray *)aBreakpoints;
+- (NSMutableArray *)history;
+- (void)setHistory:(NSMutableArray *)aHistory;
+- (NSDictionary *)profile;
+- (void)setProfile:(NSDictionary *)aProfile;
+- (NSDictionary *)woRegisters;
+- (void)setWoRegisters:(NSDictionary *)aWoRegisters;
+
+- (void)stackDoubleClicked:(id)frame;
+
+- (NSMutableDictionary *)regUpdated;
+
+- (void)cont;
+- (void)pause;
+- (void)stepInto;
+- (void)stepOut;
+- (void)stepOver;
+- (void)notifyPausedWithBreakType:(NSNumber *)aBreakType;
+- (void)enableDebugger;
+- (void)toggleBreakpoints;
+#if 0
+- (void)breakpointEnabled:(CalcCheckBreakpointArgument *)args;
+#else
+- (BOOL)breakpointEnabledAtAddress:(DWORD)dwAddr range:(DWORD)dwRange type:(UINT)nType;
+#endif
+- (void)updateDbgCycleCounter;
+- (void)clearHistory;
+@end
+
+
+@interface CalcBreakpoint : NSObject
+{
+    BOOL enabled;
+    NSNumber *address;
+    int  type;
+}
+- (BOOL)isEqual:(id)anObject;
+- (BOOL)enabled;
+- (void)setEnabled:(BOOL)value;
+- (NSNumber *)address;
+- (void)setAddress:(NSNumber *)value;
+- (int)type;
+- (void)setType:(int)value;
+@end
+
+@interface CalcCheckBreakpointArgument : NSObject
+{
+    DWORD address;
+    DWORD range;
+    UINT  type;
+    BOOL  result;
+}
+- (id)initWithAddress:(DWORD)dwAddr range:(DWORD)dwRange type:(UINT)nType;
+- (DWORD)address;
+- (DWORD)range;
+- (UINT)type;
+- (BOOL)result;
+- (void)setResult:(BOOL)value;
+@end

File CalcDebugger.m

+//
+//  CalcDebugger.m
+//  emu48
+//
+//  Created by Da Woon Jung on Thu Feb 19 2004.
+//  Copyright (c) 2004 dwj. All rights reserved.
+//
+#import "CalcDebugger.h"
+#import "DEBUGGER.H"
+#import "OPS.H"
+#import "CalcBackend.h"
+
+#define MAXCODELINES     15					// number of lines in code window
+#define MAXMEMLINES       6					// number of lines in memory window
+#define MAXMEMITEMS      16					// number of address items in a memory window line
+#define MAXBREAKPOINTS  256					// max. number of breakpoints
+#define MAXREGISTERS     22
+#define INSTRSIZE  256						// size of last instruction buffer
+
+static const char cHex[] =
+{ '0','1','2','3',
+  '4','5','6','7',
+  '8','9','A','B',
+  'C','D','E','F' };
+
+static NSString *RegToStr(BYTE *pReg, WORD wNib)
+{
+	char szBuffer[32];
+
+	WORD i;
+
+	for (i = 0;i < wNib;++i)
+		szBuffer[i] = cHex[pReg[wNib-i-1]];
+	szBuffer[i] = 0;
+
+	return [NSString stringWithUTF8String: szBuffer];
+}
+
+
+@interface CalcDebugger(Private)
+- (void)setRegUpdated:(BOOL)aUpdated forReg:(NSString *)aRegName;
+- (void)UpdateDisassemblyAtAddress:(DWORD)addr;
+- (void)UpdateRegisters;
+- (void)UpdateMemory;
+- (void)UpdateStack;
+- (void)UpdateHistory;
+- (void)UpdateMmu;
+- (void)UpdateMisc;
+- (void)UpdateProfile;
+- (void)UpdateWoRegisters;
+- (void)update;
+- (void)updateExceptDisassembly;
+- (int)checkBreakpointAtAddress:(DWORD)dwAddr range:(DWORD)dwRange type:(UINT)nType;
+@end
+
+
+@implementation CalcDebugger
+
+#if TARGET_OS_IPHONE || (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
++ (NSSet *)keyPathsForValuesAffectingDisassembly
+{
+    return [NSSet setWithObject: @"breakpoints"];
+}
+#else
++ (void)initialize
+{
+    [self setKeys:[NSArray arrayWithObject: @"breakpoints"] triggerChangeNotificationsForDependentKey:@"disassembly"];
+
+//    [self setKeys:[NSArray arrayWithObject: @"disassembly"] triggerChangeNotificationsForDependentKey:@"breakpoints"];
+    ;
+}
+#endif
+
+- (id)init
+{
+    self = [super init];
+    if (self)
+    {
+//        disassembly = [[NSMutableArray alloc] init];
+        registers   = [[NSMutableArray alloc] init];
+        memory      = [[NSMutableArray alloc] init];
+        stack       = [[NSMutableArray alloc] init];
+        breakpoints = [[NSMutableArray alloc] init];
+        history     = [[NSMutableArray alloc] init];
+//        profile     = [[NSDictionary alloc] init];
+        mmu         = [[NSMutableDictionary alloc] init];
+        misc        = [[NSMutableDictionary alloc] init];
+        regUpdated  = [[NSMutableDictionary alloc] init];
+        dbgOldState = DBG_RUN;
+        breakpointsEnabled = YES;
+    }
+    return self;
+}
+
+- (void)dealloc
+{
+    DisableDebugger();
+    [regUpdated release];
+    [misc release];
+    [mmu release];
+    [breakpoints release];
+    [history release];
+    [profile release];
+    [woRegisters release];
+    if (pdwInstrArray)					// free last instruction circular buffer
+    {
+        HeapFree(hHeap,0,pdwInstrArray);
+        pdwInstrArray = NULL;
+    }
+    [stack release];
+    [memory release];
+    [registers release];
+//    [disassembly release];
+    [super dealloc];
+}
+
+- (NSMutableArray *)disassembly
+{
+    DWORD addr = disassemblyStartAddress;
+    int i, j;
+	char szAddress[64];
+    int breakpointStatus = -1;
+    NSMutableDictionary *codeLine;
+    
+    NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity: MAXCODELINES];
+    
+	for (i = 0; i < MAXCODELINES; ++i)
+	{
+		j = sprintf(szAddress,
+                    (addr == Chipset.pc) ? "%05lX-%c " : "%05lX   ",
+                    addr,breakType ? 'R' : '>');
+        breakpointStatus = [self checkBreakpointAtAddress:addr range:1 type:BP_EXEC];
+		addr = disassemble(addr,&szAddress[j],VIEW_SHORT);
+        codeLine = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
+                    [NSNumber numberWithInt: breakpointStatus], @"breakpointStatus",
+                    [NSString stringWithUTF8String: szAddress], @"code",
+                    nil];
+        [result addObject: codeLine];
+        [codeLine release];
+	}
+
+//    [disassembly release];
+//    disassembly = result;
+    return [result autorelease]; //disassembly;
+}
+- (void)setDisassembly:(NSMutableArray *)dummy
+{
+//    [disassembly release];
+//    disassembly = [aDissassembly retain];
+}
+- (void)setDisassemblyStartAddress:(DWORD)aDisassemblyStartAddress
+{
+    disassemblyStartAddress = aDisassemblyStartAddress;
+}
+- (NSMutableArray *)registers
+{
+    return registers;
+}
+- (void)setRegisters:(NSMutableArray *)aRegisters
+{
+    [registers release];
+    registers = [aRegisters retain];
+}
+- (NSMutableArray *)memory
+{
+    return memory;
+}
+- (void)setMemory:(NSMutableArray *)aMemory
+{
+    [memory release];
+    memory = [aMemory retain];
+}
+- (NSMutableArray *)stack
+{
+    return stack;
+}
+- (void)setStack:(NSMutableArray *)aStack
+{
+    [stack release];
+    stack = [aStack retain];
+}
+- (NSMutableArray *)breakpoints
+{
+    return breakpoints;
+}
+- (void)setBreakpoints:(NSMutableArray *)aBreakpoints
+{
+    [breakpoints release];
+    breakpoints = [aBreakpoints retain];
+}
+- (NSMutableArray *)history
+{
+    return history;
+}
+- (void)setHistory:(NSMutableArray *)aHistory
+{
+    [history release];
+    history = [aHistory retain];
+}
+- (NSDictionary *)profile
+{
+    return profile;
+}
+- (void)setProfile:(NSDictionary *)aProfile
+{
+    [profile release];
+    profile = [aProfile retain];
+}
+- (NSDictionary *)woRegisters
+{
+    return woRegisters;
+}
+- (void)setWoRegisters:(NSDictionary *)aWoRegisters
+{
+    [woRegisters release];
+    woRegisters = [aWoRegisters retain];
+}
+
+- (void)stackDoubleClicked:(id)frame
+{
+    DWORD addr = [frame unsignedIntValue];
+    [self UpdateDisassemblyAtAddress: addr];
+}
+
+- (void)UpdateDisassemblyAtAddress:(DWORD)addr
+{
+    [self setDisassemblyStartAddress: addr];
+    [self setDisassembly: nil];
+}
+
+- (void)UpdateRegisters
+{
+    NSString *buf = @"";
+    BOOL isUpdated = NO;
+    NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity: MAXREGISTERS];
+#if !TARGET_OS_IPHONE
+    NSDictionary *attributes = [NSDictionary dictionaryWithObject:[NSColor redColor] forKey:NSForegroundColorAttributeName];
+#endif
+
+#if TARGET_OS_IPHONE
+    #define RegAddStr(s)    [result addObject:s]
+#else
+    #define RegAddStr(s)    [result addObject:[[[NSAttributedString alloc] initWithString:s attributes:(isUpdated?attributes:nil)] autorelease]]
+#endif
+
+#define RegCaseMem(x,f,w)  isUpdated=memcmp(x,Old##x,sizeof(x))!=0;\
+    buf=[NSString stringWithFormat:f,RegToStr(x,w)];\
+    RegAddStr(buf)
+#define RegCaseVal(x,f)    isUpdated=x!=Old##x;\
+    buf=[NSString stringWithFormat:f,x];\
+    RegAddStr(buf)
+#define RegCaseBit(x)      isUpdated=((Chipset.HST^OldChipset.HST)&x)!=0;\
+    buf=[NSString stringWithFormat:@"%s=%d",#x,(Chipset.HST&x)!=0];\
+    RegAddStr(buf)
+
+    RegCaseMem(Chipset.A,@"A= %@",16);
+    RegCaseMem(Chipset.B,@"B= %@",16);
+    RegCaseMem(Chipset.C,@"C= %@",16);
+    RegCaseMem(Chipset.D,@"D= %@",16);
+    RegCaseMem(Chipset.R0,@"R0=%@",16);
+    RegCaseMem(Chipset.R1,@"R1=%@",16);
+    RegCaseMem(Chipset.R2,@"R2=%@",16);
+    RegCaseMem(Chipset.R3,@"R3=%@",16);
+    RegCaseMem(Chipset.R4,@"R4=%@",16);
+    RegCaseVal(Chipset.d0,@"D0=%05X");
+    RegCaseVal(Chipset.d1,@"D1=%05X");
+    RegCaseVal(Chipset.P,@"P=%X");
+    RegCaseVal(Chipset.pc,@"PC=%05X");
+    RegCaseVal(Chipset.out,@"OUT=%03X");
+    RegCaseVal(Chipset.in,@"IN=%04X");
+    RegCaseMem(Chipset.ST,@"ST=%@",4);
+    RegCaseVal(Chipset.carry,@"CY=%d");
+    isUpdated = Chipset.mode_dec != OldChipset.mode_dec;
+    buf=[NSString stringWithFormat:@"Mode=%c",Chipset.mode_dec ? 'D' : 'H'];
+    RegAddStr(buf);
+    RegCaseBit(MP);
+    RegCaseBit(SR);
+    RegCaseBit(SB);
+    RegCaseBit(XM);
+    [self setRegisters: result];
+    [result release];
+}
+
+- (void)UpdateMemory
+{
+    int  i,j,k;
+    BYTE byLineData[MAXMEMITEMS];
+    char szBuffer[16], szItem[4];
+    BYTE cChar;
+    NSMutableDictionary *memline;
+    NSMutableArray *bytes;
+    NSMutableArray *result = [[NSMutableArray alloc] init];
+
+	szItem[2] = 0;							// end of string
+    DWORD addr = 0; //aIndex * (MAXMEMITEMS & (512*2048 - 1));
+
+	for (i = 0; i < MAXMEMLINES; ++i)
+	{
+        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+        memline = [NSMutableDictionary dictionaryWithCapacity: 3];
+        bytes = [NSMutableArray arrayWithCapacity: MAXMEMITEMS];
+
+        Npeek(byLineData, addr, MAXMEMITEMS);
+        [memline setObject:[NSString stringWithFormat:@"%05lX", addr] forKey:@"address"];
+        for (k = 0, j = 0; j < MAXMEMITEMS; ++j)
+        {
+            // read from fetched data line
+            szItem[j&0x1] = cHex[byLineData[j]];
+            // characters are saved in LBS, MSB order
+            cChar = (cChar >> 4) | (byLineData[j] << 4);
+            
+            if ((j&0x1) != 0)
+            {
+                // byte field
+                [bytes addObject: [NSString stringWithUTF8String: szItem]];
+                
+                // text field
+                szBuffer[j/2] = (isprint(cChar) != 0) ? cChar : '.';
+            }
+        }
+        szBuffer[j/2] = 0;					// end of text string
+        [memline setObject:[bytes componentsJoinedByString: @" "] forKey:@"bytes"];
+        [memline setObject:[NSString stringWithUTF8String: szBuffer] forKey:@"text"];
+        [result addObject: memline];
+        addr = (addr + MAXMEMITEMS) & (512*2048 - 1);
+        [pool release];
+    }
+    [self setMemory: result];
+    [result release];
+}
+
+- (void)UpdateStack
+{
+    int i;
+    NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity: ARRAYSIZEOF(Chipset.rstk)];
+    for (i = 1; i <= ARRAYSIZEOF(Chipset.rstk); ++i)
+    {
+        [result addObject: [NSDictionary dictionaryWithObjectsAndKeys:
+                            [NSString stringWithFormat: @"%d: %05X", i, Chipset.rstk[(Chipset.rstkp-i)&7]], @"displayString",
+                            [NSNumber numberWithUnsignedInt: Chipset.rstk[(Chipset.rstkp-i)&7]], @"address",
+                            nil]];
+    }
+    [self setStack: result];
+    [result release];
+}
+
+- (void)UpdateHistory
+{
+    int i, j;
+	char szBuffer[64];
+    NSString *addr;
+    NSMutableArray *result = [[NSMutableArray alloc] initWithCapacity: INSTRSIZE];
+    if (nil == pdwInstrArray)
+    {
+		pdwInstrArray = HeapAlloc(hHeap,0,INSTRSIZE*sizeof(*pdwInstrArray));
+		wInstrSize = INSTRSIZE;				// size of last instruction array
+		wInstrWp = wInstrRp = 0;			// write/read pointer
+    }
+
+    for (i = wInstrRp; i != wInstrWp; i = (i + 1) % wInstrSize)
+    {
+        j = sprintf(szBuffer, "%05X   ", pdwInstrArray[i]);
+        disassemble(pdwInstrArray[i],&szBuffer[j],VIEW_SHORT);
+        addr = [[NSString alloc] initWithUTF8String: szBuffer];
+        [result addObject: addr];
+        [addr release];
+    }
+    [self setHistory: result];
+    [result release];
+}
+
+- (void)UpdateMmu
+{
+	if (Chipset.IOCfig)
+		[mmu setObject:[NSString stringWithFormat: @"%05X", Chipset.IOBase]
+                forKey:@"MMU_IO_A"];
+	if (Chipset.P0Cfig)
+		[mmu setObject:[NSString stringWithFormat: @"%05X",Chipset.P0Base<<12]
+                forKey:@"MMU_NCE2_A"];
+	if (Chipset.P0Cfg2)
+		[mmu setObject:[NSString stringWithFormat: @"%05X",(Chipset.P0Size^0xFF)<<12]
+                forKey:@"MMU_NCE2_S"];
+	if (Chipset.P1Cfig)
+		[mmu setObject:[NSString stringWithFormat:@"%05X",Chipset.P1Base<<12]
+                forKey:(cCurrentRomType=='S') ? @"MMU_CE1_A" : @"MMU_CE2_A"];
+    if (Chipset.P1Cfg2)
+        [mmu setObject:[NSString stringWithFormat:@"%05X",(Chipset.P1Size^0xFF)<<12]
+                forKey:(cCurrentRomType=='S') ? @"MMU_CE1_S" : @"MMU_CE2_S"];
+    if (Chipset.P2Cfig)
+        [mmu setObject:[NSString stringWithFormat:@"%05X",Chipset.P2Base<<12]
+                forKey:(cCurrentRomType=='S') ? @"MMU_CE2_A" : @"MMU_NCE3_A"];
+    if (Chipset.P2Cfg2)
+        [mmu setObject:[NSString stringWithFormat:@"%05X",(Chipset.P2Size^0xFF)<<12]
+                forKey:(cCurrentRomType=='S') ? @"MMU_CE2_S" : @"MMU_NCE3_S"];
+    if (Chipset.BSCfig)
+        [mmu setObject:[NSString stringWithFormat:@"%05X",Chipset.BSBase<<12]
+                forKey:(cCurrentRomType=='S') ? @"MMU_NCE3_A" : @"MMU_CE1_A"];
+    if (Chipset.BSCfg2)
+        [mmu setObject:[NSString stringWithFormat:@"%05X",(Chipset.BSSize^0xFF)<<12]
+                forKey:(cCurrentRomType=='S') ? @"MMU_NCE3_S" : @"MMU_CE1_S"];
+}
+
+- (void)UpdateMisc
+{
+    [self setRegUpdated:(Chipset.inte != OldChipset.inte) forReg:@"MISC_INT"];
+	[misc setObject:Chipset.inte ? @"On " : @"Off" forKey:@"MISC_INT"];
+
+    [self setRegUpdated:(Chipset.intk != OldChipset.intk) forReg:@"MISC_KEY"];
+	[misc setObject:Chipset.intk ? @"On " : @"Off" forKey:@"MISC_KEY"];
+
+    [self setRegUpdated:NO forReg:@"MISC_BS"];
+	// not 38/48S // CdB for HP: add Apples type
+	if (cCurrentRomType!='A' && cCurrentRomType!='S')
+    {
+        [self setRegUpdated:((Chipset.Bank_FF & 0x7F) != (OldChipset.Bank_FF & 0x7F)) forReg:@"MISC_BS"];
+        [misc setObject:[NSString stringWithFormat: @"%02X",Chipset.Bank_FF & 0x7F]
+                 forKey:@"MISC_BS"];
+    }
+    else
+    {
+        [misc removeObjectForKey: @"MISC_BS"];
+    }
+}
+
+- (void)UpdateProfile
+{
+#define CPU_FREQ 524288					// base CPU frequency
+#define SX_RATE  0x0E
+#define GX_RATE  0x1B
+#define GP_RATE  0x1B*3 // CdB for HP: add high speed apples
+#define G2_RATE  0x1B*2 // CdB for HP: add low speed apples
+    NSDictionary *result;
+    NSString *lastCycles;
+    NSString *lastTime;
+
+    LPCTSTR pcUnit[] = { _T("s"),_T("ms"),_T("us"),_T("ns") };
+
+    DWORD lVar;
+    INT   i;
+    DWORD dwFreq, dwEndFreq;
+
+    // 64 bit cpu cycle counter
+    lVar = Chipset.cycles - OldChipset.cycles;
+
+    // cycle counts
+    lastCycles = [[NSString alloc] initWithFormat:@"%u", lVar];
+
+    // CPU frequency
+    switch (cCurrentRomType) // CdB for HP: add apples speed selection
+    {
+        case 'S': dwFreq= ((SX_RATE + 1) * CPU_FREQ / 4); break;
+        case 'X': case 'G': case 'E': case 'A': dwFreq= ((GX_RATE + 1) * CPU_FREQ / 4); break;
+        case 'P': case 'Q': dwFreq= ((GP_RATE + 1) * CPU_FREQ / 4); break;
+        case '2': dwFreq= ((G2_RATE + 1) * CPU_FREQ / 4); break;
+    }
+    dwEndFreq = ((999 * 2 - 1) * dwFreq) / (2 * 1000);
+
+    // search for unit
+    for (i = 0; i < ARRAYSIZEOF(pcUnit) - 1; ++i)
+    {
+        if (lVar > dwEndFreq) break;		// found ENG unit
+        lVar *= 1000;						// next ENG unit
+    }
+
+    // calculate rounded time
+    lVar = (2 * lVar + dwFreq) / (2 * dwFreq);
+    
+    _ASSERT(i < ARRAYSIZEOF(pcUnit));
+    lastTime = [[NSString alloc] initWithFormat:@"%u %s", lVar,pcUnit[i]];
+    result = [[NSDictionary alloc] initWithObjectsAndKeys:
+               lastCycles, @"lastCycles",
+               lastTime,   @"lastTime",
+               nil];
+    [lastCycles release];
+    [lastTime   release];
+    [self setProfile: result];
+    [result release];
+#undef SX_CLK
+#undef GX_CLK
+#undef GP_RATE
+#undef G2_RATE
+#undef CPU_FREQ
+}
+
+- (void)UpdateWoRegisters
+{
+    NSDictionary *result = [[NSDictionary alloc] initWithObjectsAndKeys:
+        [NSString stringWithFormat: @"%05X", Chipset.start1], @"ADDR20_24",
+        [NSString stringWithFormat: @"%05X", Chipset.loffset], @"ADDR25_27",
+        [NSString stringWithFormat: @"%05X", Chipset.lcounter], @"ADDR28_29",
+        [NSString stringWithFormat: @"%05X", Chipset.start2], @"ADDR30_34",
+    nil];
+    [self setWoRegisters: result];
+    [result release];
+}
+
+- (NSMutableDictionary *)regUpdated
+{
+    return regUpdated;
+}
+- (void)setRegUpdated:(BOOL)aUpdated forReg:(NSString *)aRegName
+{
+#if TARGET_OS_IPHONE
+    [regUpdated setObject:aRegName forKey:aRegName];
+#else
+    [regUpdated setObject:(aUpdated ? [NSColor redColor] : [NSColor textColor]) forKey:aRegName];
+#endif
+}
+
+- (void)update
+{
+    [self UpdateDisassemblyAtAddress: Chipset.pc];
+    [self updateExceptDisassembly];
+}
+
+- (void)updateExceptDisassembly
+{
+    [self UpdateRegisters];
+    [self UpdateMemory];
+    [self UpdateStack];
+    [self UpdateHistory];
+    [self UpdateMmu];
+    [self UpdateMisc];
+    [self UpdateProfile];
+    [self UpdateWoRegisters];
+}
+
+- (void)cont
+{
+    if (nDbgState != DBG_RUN)				// emulation stopped
+    {
+        if ([breakpoints count] > 0)
+            nDbgState = DBG_RUN;			// state "run"
+        else
+            nDbgState = DBG_OFF;
+        [self update];
+		OldChipset = Chipset;				// save chipset values
+		SetEvent(hEventDebug);				// run emulation
+    }
+}
+- (void)pause
+{
+    dwDbgStopPC = -1;					// no stop address for goto cursor
+    dwDbgRplPC = -1;					// no stop address for RPL breakpoint
+
+    // init reference cpu cycle counter for 64 bit debug cycle counter
+    dbgRefCycles = (DWORD) (Chipset.cycles & 0xFFFFFFFF);
+    
+	nDbgState = DBG_STEPINTO;				// state "step into"
+    if (Chipset.Shutdn)					// cpu thread stopped
+        SetEvent(hEventShutdn);			// goto debug session
+    [self update];
+    OldChipset = Chipset;				// save chipset values
+}
+- (void)stepInto
+{
+	if (nDbgState != DBG_RUN)				// emulation stopped
+	{
+        //		if (bDbgSkipInt)					// skip code in interrupt handler
+        //			DisableMenuKeys(hDlg);			// disable menu keys
+        
+		nDbgState = DBG_STEPINTO;			// state "step into"
+        [self update];
+		OldChipset = Chipset;				// save chipset values
+		SetEvent(hEventDebug);				// run emulation
+	}
+}
+- (void)stepOut
+{
+	if (nDbgState != DBG_RUN)				// emulation stopped
+	{
+        //		DisableMenuKeys(hDlg);				// disable menu keys
+		dbgRstkp = (Chipset.rstkp-1)&7;	// save stack data
+		dwDbgRstk  = Chipset.rstk[dbgRstkp];
+		nDbgState = DBG_STEPOUT;			// state "step out"
+        [self update];
+		OldChipset = Chipset;				// save chipset values
+		SetEvent(hEventDebug);				// run emulation
+	}
+}
+- (void)stepOver
+{
+	if (nDbgState != DBG_RUN)				// emulation stopped
+	{
+		LPBYTE I = FASTPTR(Chipset.pc);
+        
+        //		if (bDbgSkipInt)					// skip code in interrupt handler
+        //			DisableMenuKeys(hDlg);			// disable menu keys
+        
+		dbgRstkp = Chipset.rstkp;			// save stack level
+        
+		// GOSUB 7aaa, GOSUBL 8Eaaaa, GOSBVL 8Faaaaa
+		if (I[0] == 0x7 || (I[0] == 0x8 && (I[1] == 0xE || I[1] == 0xF)))
+		{
+			nDbgState = DBG_STEPOVER;		// state "step over"
+		}
+		else
+		{
+			nDbgState = DBG_STEPINTO;		// state "step into"
+		}
+        [self update];
+		OldChipset = Chipset;				// save chipset values
+		SetEvent(hEventDebug);				// run emulation
+	}
+}
+
+- (void)notifyPausedWithBreakType:(NSNumber *)aBreakType
+{
+	nDbgState = DBG_STEPINTO;				// state "step into"
+	dwDbgStopPC = -1;						// disable "cursor stop address"
+    breakType = [aBreakType intValue];
+    [self update];
+}
+
+- (void)enableDebugger
+{
+    if (DBG_OFF == nDbgState)
+    {
+        nDbgState = DBG_RUN;
+        [self UpdateDisassemblyAtAddress: Chipset.pc];
+    }
+    else
+    {
+        [self setDisassembly: nil];
+    }
+    [self updateExceptDisassembly];
+}
+
+- (void)toggleBreakpoints
+{
+    breakpointsEnabled = !breakpointsEnabled;
+    [self enableDebugger];
+}
+
+- (int)checkBreakpointAtAddress:(DWORD)dwAddr range:(DWORD)dwRange type:(UINT)nType
+{
+    static int BREAKPOINT_TYPE[] = { BP_EXEC, BP_RPL, BP_ACCESS, BP_READ, BP_WRITE };
+    NSEnumerator *e;
+    id breakpoint;
+
+    e = [breakpoints objectEnumerator];
+    while ((breakpoint = [e nextObject]))
+	{
+		// check address range and type
+		if (   [[breakpoint valueForKey: @"address"] intValue] >= dwAddr 
+            && [[breakpoint valueForKey: @"address"] intValue] < dwAddr + dwRange
+			&& (BREAKPOINT_TYPE[[[breakpoint valueForKey: @"type"] intValue]] & nType) != 0)
+        {
+            return [[breakpoint valueForKey: @"enabled"] boolValue] && breakpointsEnabled;
+        }
+	}
+    return -1;
+}
+
+- (BOOL)breakpointEnabledAtAddress:(DWORD)dwAddr range:(DWORD)dwRange type:(UINT)nType
+{
+    return (1 == [self checkBreakpointAtAddress:dwAddr range:dwRange type:nType]); 
+}
+
+- (void)updateDbgCycleCounter
+{
+	// update 64 bit cpu cycle counter
+	if (Chipset.cycles < dbgRefCycles) ++Chipset.cycles_reserved;
+	dbgRefCycles = (DWORD) (Chipset.cycles & 0xFFFFFFFF);
+}
+
+- (void)clearHistory
+{
+    if (pdwInstrArray)					// free last instruction circular buffer
+    {
+        HeapFree(hHeap,0,pdwInstrArray);
+        pdwInstrArray = NULL;
+    }
+    [self setHistory: nil];
+}
+@end
+
+
+@implementation CalcBreakpoint
+
+- (id)init
+{
+    self = [super init];
+    if (self)
+    {
+        [self setEnabled: YES];
+        [self setType:    0];
+    }
+    return self;
+}
+
+- (void)dealloc
+{
+    [address release];
+    [super dealloc];
+}
+
+- (BOOL)isEqual:(id)anObject
+{
+#if TARGET_OS_IPHONE
+    return [[self address] isEqualToNumber: [anObject address]];
+#else
+    return [[self address] isEqualTo: [anObject address]];
+#endif
+}
+
+- (BOOL)enabled { return enabled; }
+- (void)setEnabled:(BOOL)value { enabled = value; }
+- (NSNumber *)address  { return address; }
+- (void)setAddress:(NSNumber *)value
+{
+    [address release];
+    address = [value retain];
+}
+- (int)type     { return type;    }
+- (void)setType:(int)value     { type = value; }
+@end
+
+@implementation CalcCheckBreakpointArgument
+- (id)initWithAddress:(DWORD)dwAddr range:(DWORD)dwRange type:(UINT)nType
+{
+    self = [super init];
+    address = dwAddr;
+    range   = dwRange;
+    type    = nType;
+    return self;
+}
+- (DWORD)address { return address; }
+- (DWORD)range   { return range; }
+- (UINT)type   { return type; }
+- (BOOL)result { return result; }
+- (void)setResult:(BOOL)value { result = value; }
+@end
+
+
+VOID UpdateDbgCycleCounter(VOID)
+{
+    // Currently not used
+#if 0
+    [[[CalcBackend sharedBackend] debugModel] performSelectorOnMainThread:@selector(updateDbgCycleCounter) withObject:nil waitUntilDone:YES];
+#endif
+}
+
+BOOL CheckBreakpoint(DWORD dwAddr, DWORD dwRange, UINT nType)
+{
+    return [[[CalcBackend sharedBackend] debugModel] breakpointEnabledAtAddress:dwAddr range:dwRange type:nType];
+}
+
+VOID NotifyDebugger(INT nType)
+{
+    [[[CalcBackend sharedBackend] debugModel] performSelectorOnMainThread:@selector(notifyPausedWithBreakType:) withObject:[NSNumber numberWithInt: nType] waitUntilDone:YES];
+}
+
+VOID DisableDebugger(VOID)
+{
+    nDbgState = DBG_OFF;				// debugger inactive
+    bInterrupt = TRUE;					// exit opcode loop
+    SetEvent(hEventDebug);
+}

File CalcPrefController.h

+//
+//  CalcPrefController
+//  emu48
+//
+//  Created by Da-Woon Jung on Thu Feb 19 2004.
+//  Copyright (c) 2004 dwj. All rights reserved.
+//
+
+#define CALC_RES_PATH       @"Calculators"
+#define CALC_USER_PATH      @"Emu48"
+#define CALC_STATE_PATH     @"States"
+#define CALC_DEFAULT_STATE  @"state.e48"
+
+enum {
+    kHPMnemonics, kClassMnemonics
+};
+
+enum {
+    kBeepSystem, kBeepWave
+};
+
+
+@interface CalcPrefController : NSObject
+{
+    NSArray *standardCalcs;
+    NSMutableArray *calculators;
+    int defaultCalculator;
+}
++ (void)registerDefaults;
++ (NSDictionary *)cleanDefaults;
++ (void)resetDefaults;
+
+- (int)DefaultCalculator;
+- (void)setDefaultCalculator:(int)aIndex;
+
+- (BOOL)RealSpeed;
+- (BOOL)Grayscale;
+- (BOOL)AlwaysOnTop;
+- (BOOL)AutoSaveOnExit;
+- (BOOL)ReloadFiles;
+- (BOOL)LoadObjectWarning;
+- (BOOL)AlwaysDisplayLog;
+- (BOOL)RomWriteable;
+- (int)Mnemonics;
+- (int)WaveBeep;
+- (float)WaveVolume;
+- (void)setRealSpeed:(BOOL)value;
+- (void)setGrayscale:(BOOL)value;
+- (void)setAlwaysOnTop:(BOOL)value;
+- (void)setAutoSaveOnExit:(BOOL)value;
+- (void)setReloadFiles:(BOOL)value;
+- (void)setLoadObjectWarning:(BOOL)value;
+- (void)setAlwaysDisplayLog:(BOOL)value;
+- (void)setRomWriteable:(BOOL)value;
+- (void)setMnemonics:(int)value;
+- (void)setWaveBeep:(int)value;
+- (void)setWaveVolume:(float)value;
+
+- (BOOL)Port1Plugged;
+- (BOOL)Port1Writeable;
+- (BOOL)Port2IsShared;
+- (NSString *)Port2Filename;
+- (BOOL)Port1Enabled;
+- (BOOL)Port2Enabled;
+- (void)setPort1Plugged:(BOOL)value;
+- (void)setPort1Writeable:(BOOL)value;
+- (void)setPort2IsShared:(BOOL)value;
+- (void)setPort2Filename:(NSString *)value;
+- (void)setPort1Enabled:(BOOL)value;
+- (void)setPort2Enabled:(BOOL)value;
+
++ (NSArray *)calculatorsAtPath:(NSString *)aPath
+                relativeToPath:(NSString *)base;
+- (NSMutableArray *)calculators;
+- (void)setCalculators:(NSArray *)aCalculators;
+@end

File CalcPrefController.m

+//
+//  CalcPrefController.m
+//  emu48
+//
+//  Created by Da-Woon Jung on Thu Feb 19 2004.
+//  Copyright (c) 2004 dwj. All rights reserved.
+//
+#import "CalcPrefController.h"
+#import "pch.h"
+#import "EMU48.H"
+#import "IO.H"
+#import "files.h"
+#import "kmlparser.h"
+#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 30000
+#import <MobileCoreServices/MobileCoreServices.h>
+#endif
+
+@interface CalcPrefController(Private)
+- (void)refreshPort1WithPluggedStatus:(BOOL)isPlugged writeable:(BOOL)isWriteable;
+- (void)refreshPort2WithFilename:(NSString *)aFilename;
+- (void)refreshCalculators:(id)aArg;
+@end
+
+
+@implementation CalcPrefController
+
+- (id)init
+{
+    self = [super init];
+    if (self)
+    {
+        calculators = [[NSMutableArray alloc] init];
+        standardCalcs = [[[self class] calculatorsAtPath:CALC_RES_PATH relativeToPath:[[NSBundle mainBundle] resourcePath]] retain];
+        if (standardCalcs)
+        {
+            NSEnumerator *standardCalcEnum = [standardCalcs objectEnumerator];
+            NSNumber *readonly = [[NSNumber alloc] initWithBool: YES];
+            id standardCalc;
+            while ((standardCalc = [standardCalcEnum nextObject]))
+                [standardCalc setObject:readonly forKey:@"readonly"];
+            [readonly release];
+//            [self setCalculators: standardCalcs];
+        }
+        [self refreshCalculators: nil];
+    }
+    return self;
+}
+
+- (void)dealloc
+{
+    [calculators release];
+    [standardCalcs release];
+    [super dealloc];
+}
+
++ (void)registerDefaults
+{
+    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+    [defaults registerDefaults: [CalcPrefController cleanDefaults]];
+}
+
++ (NSDictionary *)cleanDefaults
+{
+    NSString *errorDesc = nil;
+    NSPropertyListFormat format;
+    NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"defaults" ofType:@"plist"];
+    NSData *plistData = [[NSFileManager defaultManager] contentsAtPath:plistPath];
+    NSDictionary *defaults = (NSDictionary *)[NSPropertyListSerialization propertyListFromData:plistData mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&errorDesc];
+    return defaults;
+}
+
++ (void)resetDefaults
+{
+    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+    NSDictionary *cleanDefaults = [CalcPrefController cleanDefaults];
+    NSEnumerator *keyEnum = [cleanDefaults keyEnumerator];
+    NSString *key;
+    while ((key = [keyEnum nextObject]))
+    {
+        [defaults setObject:[cleanDefaults objectForKey:key] forKey:key];
+    }
+}
+
+/*
++ (NSDictionary *)volatileDefaults
+{
+    return [NSDictionary dictionaryWithObjectsAndKeys:
+            [NSNumber numberWithBool:NO],  @"Port1Plugged",
+            [NSNumber numberWithBool:NO],  @"Port1Writeable",
+            [NSNumber numberWithBool:NO],  @"Port1Enabled",
+            [NSNumber numberWithBool:NO],  @"Port2Enabled",
+            nil];
+}
+*/
+
+- (int)DefaultCalculator
+{
+    return defaultCalculator;
+}
+
+- (void)setDefaultCalculator:(int)aIndex
+{
+    NSArray *allCalcs = [self calculators];
+    int count = [allCalcs count]; 
+    NSDictionary *calc;
+    if (aIndex < count)
+    {
+        defaultCalculator = aIndex;
+        calc = [allCalcs objectAtIndex: aIndex];
+        [[NSUserDefaults standardUserDefaults] setObject:[calc objectForKey: @"path"] forKey: @"DefaultCalculator"];
+    }
+    else
+    {
+        defaultCalculator = 0;
+        [[NSUserDefaults standardUserDefaults] removeObjectForKey: @"DefaultCalculator"];
+    }
+}
+
+#define USERDEFAULTS_ACCESSOR_BOOL(n)  -(BOOL)n{return [[NSUserDefaults standardUserDefaults] boolForKey:@#n];} \
+    -(void)set##n:(BOOL)value{[[NSUserDefaults standardUserDefaults] setBool:value forKey:@#n];}
+
+#define USERDEFAULTS_ACCESSOR_INT(n)   -(int)n{return [[NSUserDefaults standardUserDefaults] integerForKey:@#n];} \
+    -(void)set##n:(int)value{[[NSUserDefaults standardUserDefaults] setInteger:value forKey:@#n];}
+
+USERDEFAULTS_ACCESSOR_BOOL(RealSpeed)
+USERDEFAULTS_ACCESSOR_BOOL(Grayscale)
+USERDEFAULTS_ACCESSOR_BOOL(AlwaysOnTop)
+USERDEFAULTS_ACCESSOR_BOOL(AutoSaveOnExit)
+USERDEFAULTS_ACCESSOR_BOOL(ReloadFiles)
+USERDEFAULTS_ACCESSOR_BOOL(LoadObjectWarning)
+USERDEFAULTS_ACCESSOR_BOOL(AlwaysDisplayLog)
+USERDEFAULTS_ACCESSOR_BOOL(RomWriteable)
+USERDEFAULTS_ACCESSOR_INT(Mnemonics)
+USERDEFAULTS_ACCESSOR_INT(WaveBeep)
+
+- (float)WaveVolume { return [[NSUserDefaults standardUserDefaults] floatForKey: @"WaveVolume"]; }
+- (void)setWaveVolume:(float)value { [[NSUserDefaults standardUserDefaults] setFloat:value forKey:@"WaveVolume"]; }
+
+- (BOOL)Port1Plugged
+{
+    if (cCurrentRomType=='S' || cCurrentRomType=='G' || cCurrentRomType==0)
+    {
+        return ((Chipset.cards_status & PORT1_PRESENT) != 0);
+    }
+    return NO;
+}
+- (BOOL)Port1Writeable
+{
+    if (cCurrentRomType=='S' || cCurrentRomType=='G' || cCurrentRomType==0)
+    {
+        return ((Chipset.cards_status & PORT1_WRITE) != 0);
+    }
+    return NO;
+}
+- (BOOL)Port2IsShared
+{
+    return [[NSUserDefaults standardUserDefaults] boolForKey: @"Port2IsShared"];
+}
+- (NSString *)Port2Filename
+{
+    return [[NSUserDefaults standardUserDefaults] stringForKey: @"Port2Filename"];
+}
+
+- (BOOL)Port1Enabled
+{
+    if (cCurrentRomType=='S' || cCurrentRomType=='G' || cCurrentRomType==0)
+    {
+        if (nState != SM_INVALID)		// Invalid State
+            return YES;
+    }
+    return NO;
+}
+- (BOOL)Port2Enabled
+{
+    if (cCurrentRomType=='S' || cCurrentRomType=='G' || cCurrentRomType==0)
+    {
+        return YES;
+    }
+    return NO;
+}
+- (void)setPort1Plugged:(BOOL)value
+{
+    [self refreshPort1WithPluggedStatus:value writeable:[self Port1Writeable]];
+}
+- (void)setPort1Writeable:(BOOL)value
+{
+    [self refreshPort1WithPluggedStatus:[self Port1Plugged] writeable:value];
+}
+- (void)setPort2IsShared:(BOOL)value
+{
+    [[NSUserDefaults standardUserDefaults] setBool:value forKey:@"Port2IsShared"];
+    [self refreshPort2WithFilename: [self Port2Filename]];
+}
+- (void)setPort2Filename:(NSString *)value
+{
+    [[NSUserDefaults standardUserDefaults] setObject:value forKey:@"Port2Filename"];
+    [self refreshPort2WithFilename: value];
+}
+- (void)setPort1Enabled:(BOOL)value
+{
+}
+- (void)setPort2Enabled:(BOOL)value
+{
+}
+- (void)refreshPort1WithPlugged:(BOOL)isPlugged writeable:(BOOL)isWriteable
+{
+    if (Chipset.Port1Size && (cCurrentRomType!='X' || cCurrentRomType!='2' || cCurrentRomType!='Q'))   // CdB for HP: add apples
+    {
+        UINT nOldState = SwitchToState(SM_SLEEP);
+        // save old card status
+        BYTE bCardsStatus = Chipset.cards_status;
+
+        // port1 disabled?
+        Chipset.cards_status &= ~(PORT1_PRESENT | PORT1_WRITE);
+        if (isPlugged)
+        {
+            Chipset.cards_status |= PORT1_PRESENT;
+            if (isWriteable)
+                Chipset.cards_status |= PORT1_WRITE;
+        }
+
+        // changed card status in slot1?
+        if (   ((bCardsStatus ^ Chipset.cards_status) & (PORT1_PRESENT | PORT1_WRITE)) != 0
+            && (Chipset.IORam[CARDCTL] & ECDT) != 0 && (Chipset.IORam[TIMER2_CTRL] & RUN) != 0
+            )
+        {
+            Chipset.HST |= MP;			// set Module Pulled
+            IOBit(SRQ2,NINT,FALSE);		// set NINT to low
+            Chipset.SoftInt = TRUE;		// set interrupt
+            bInterrupt = TRUE;
+        }
+        SwitchToState(nOldState);
+    }
+}
+- (void)refreshPort2WithFilename:(NSString *)aFilename
+{
+    UINT nOldState = SwitchToState(SM_INVALID);
+
+    UnmapPort2();				// unmap port2
+
+    if (cCurrentRomType)		// ROM defined
+    {
+        MapPort2([aFilename UTF8String]);
+
+        // port2 changed and card detection enabled
+        if (   (Chipset.wPort2Crc != wPort2Crc)
+            && (Chipset.IORam[CARDCTL] & ECDT) != 0 && (Chipset.IORam[TIMER2_CTRL] & RUN) != 0
+            )
+        {
+            Chipset.HST |= MP;		// set Module Pulled
+            IOBit(SRQ2,NINT,FALSE);	// set NINT to low
+            Chipset.SoftInt = TRUE;	// set interrupt
+            bInterrupt = TRUE;
+        }
+        // save fingerprint of port2
+        Chipset.wPort2Crc = wPort2Crc;
+    }
+    SwitchToState(nOldState);
+}
+
++ (NSArray *)calculatorsAtPath:(NSString *)aCalcPath
+                relativeToPath:(NSString *)base
+{
+    NSMutableArray *result = nil;
+    NSFileManager *fm = [NSFileManager defaultManager];
+    BOOL isFolder = NO;
+    NSString *calcPath = base ? [base stringByAppendingPathComponent: aCalcPath] : aCalcPath;
+    if (![fm fileExistsAtPath:calcPath isDirectory:&isFolder] || !isFolder)
+        return nil;
+    NSDirectoryEnumerator *dirEnum = [fm enumeratorAtPath: calcPath];
+    NSString *kmlFile;
+    NSString *kmlExt = (NSString *)UTTypeCopyPreferredTagWithClass((CFStringRef)@"com.dw.emu48-kml", kUTTagClassFilenameExtension);
+    if (nil==kmlExt)
+        return nil;
+    KmlParser *parser = [[KmlParser alloc] init];
+    result = [NSMutableArray array];
+    while ((kmlFile = [dirEnum nextObject]))
+    {
+        if (NSOrderedSame != [[kmlFile pathExtension] caseInsensitiveCompare: kmlExt])
+            continue;
+        NSString *kmlPath = [calcPath stringByAppendingPathComponent: kmlFile];
+        KmlParseResult *kml = [parser LoadKMLGlobal: kmlPath];
+        if (nil == kml) continue;
+        NSString *title = [kml stringForBlockId:TOK_GLOBAL commandId:TOK_TITLE atIndex:0];
+        if (nil == title)
+            title = NSLocalizedString(@"Untitled", @"");
+        NSString *author = [kml stringForBlockId:TOK_GLOBAL commandId:TOK_AUTHOR atIndex:0];
+        if (nil == author)
+            author = NSLocalizedString(@"<Unknown Author>", @"");
+        NSString *model = [kml stringForBlockId:TOK_GLOBAL commandId:TOK_MODEL atIndex:0];
+        if (nil == model)
+            model = @"";
+        NSString *imagePath = [kml stringForBlockId:TOK_GLOBAL commandId:TOK_BITMAP atIndex:0];
+
+        NSMutableDictionary *calc = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+                                     title,  @"title",
+                                     author, @"author",
+                                     model,  @"model",
+                                     base ? [aCalcPath stringByAppendingPathComponent: kmlFile] : kmlPath, @"path",
+                                     nil];
+        if (imagePath && [imagePath length]>0)
+        {
+            [calc setObject:[calcPath stringByAppendingPathComponent: imagePath] forKey:@"imagePath"];
+        }
+        
+        [result addObject: calc];
+    }
+    [parser release];
+    [kmlExt release];
+    return result;
+}
+
+- (NSMutableArray *)calculators
+{
+    return calculators;
+}
+
+- (void)setCalculators:(NSArray *)aCalculators
+{
+    [calculators setArray: aCalculators];
+}
+
+- (void)refreshCalculators:(id)aArg
+{
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    NSMutableArray *allCalcs = [NSMutableArray array];
+    if (standardCalcs)
+        [allCalcs addObjectsFromArray: standardCalcs];
+    // First search user's home directory (create calc folder on first run)
+    NSArray *systemPaths = NSSearchPathForDirectoriesInDomains(
+#if TARGET_OS_IPHONE
+        NSDocumentDirectory,
+#else
+        NSApplicationSupportDirectory,
+#endif
+        NSUserDomainMask, YES);
+    NSString *calcPath;
+    NSFileManager *fm = [NSFileManager defaultManager];
+    BOOL isFolder = NO;
+    if (systemPaths && [systemPaths count] > 0)
+    {
+        calcPath = [[[systemPaths objectAtIndex: 0] stringByAppendingPathComponent: CALC_USER_PATH] stringByAppendingPathComponent: CALC_RES_PATH];
+        if (![fm fileExistsAtPath:calcPath isDirectory:&isFolder])
+        {
+            NSArray *pathComponents = [calcPath pathComponents];
+            NSString *parentPath = @"";
+            NSString *pathComp;
+            NSEnumerator *pathEnum = [pathComponents objectEnumerator];
+            while ((pathComp = [pathEnum nextObject]))
+            {
+                parentPath = [parentPath stringByAppendingPathComponent: pathComp];
+                if (![fm fileExistsAtPath:parentPath isDirectory:&isFolder])
+                    [fm createDirectoryAtPath:parentPath attributes:nil];
+            }
+        }
+        else if (!isFolder)
+            return;
+        NSArray *userCalcs = [[self class] calculatorsAtPath:calcPath relativeToPath:nil];
+        if (userCalcs && [userCalcs count]>0)
+        {
+            [allCalcs addObjectsFromArray: userCalcs];
+        }
+    }
+#if !TARGET_OS_IPHONE
+    // Add all calcs found in system directories too
+    systemPaths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSLocalDomainMask | NSNetworkDomainMask, YES);
+    NSEnumerator *dirEnum = [systemPaths objectEnumerator];
+    while ((calcPath = [dirEnum nextObject]))
+    {
+        NSArray *userCalcs = [[self class] calculatorsAtPath:[[calcPath stringByAppendingPathComponent: CALC_USER_PATH] stringByAppendingPathComponent: CALC_RES_PATH] relativeToPath:nil];
+        if (userCalcs && [userCalcs count]>0)
+        {
+            [allCalcs addObjectsFromArray: userCalcs];
+        }
+    }
+#endif
+
+    [self setCalculators: allCalcs];
+
+    int result = 0;
+    id path = [[NSUserDefaults standardUserDefaults] objectForKey: @"DefaultCalculator"];
+    if (path && [path isKindOfClass: [NSString class]])
+    {
+        int i;
+        int count = [allCalcs count];
+        NSDictionary *calc;
+        for (i = 0; i < count; ++i)
+        {
+            calc = [allCalcs objectAtIndex: i];
+            if ([[calc objectForKey: @"path"] isEqualToString: path])
+            {
+                result = i;
+                break;
+            }
+        }
+    }
+    defaultCalculator = result;
+
+    [pool release];
+}
+@end

File Core/APPLE.C