home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
World of A1200
/
World_Of_A1200.iso
/
programs
/
emulator
/
appleonamiga
/
txt
/
apple.mod
< prev
next >
Wrap
Text File
|
1995-02-27
|
46KB
|
1,882 lines
MODULE Apple;
(*
Apple 2 emulator, written and put into the public domain 1993 by Claudio Nieder.
Current emulation: Apple 2 with RAM, ROM, text screen and floppy.
*)
(*$
DEFINE Debug:=TRUE
DEFINE Track:=TRUE
DEFINE Commands:=FALSE
*)
(*$
OverflowChk:=FALSE
RangeChk:=FALSE
StackChk:=FALSE
EntryClear:=FALSE
Volatile:=FALSE
StackParms:=FALSE
NilChk:=FALSE
*)
FROM SYSTEM IMPORT ADR,CAST,LONGSET,TAG;
FROM Base37 IMPORT A,B,N;
IMPORT
AS:AppleScreen,d:DosD,D:DosL,e:ExecD,E:ExecL,Heap,R;
(*$ IF Debug *)
IMPORT
Break,T:Terminal;
(*$ ENDIF *)
CONST
basicRomFile="PROGDIR:BASIC";
diskPromFile="PROGDIR:DISK";
errNoBasic=basicRomFile+" not found";
errReadBasic="Error reading "+basicRomFile;
errNoDisk=diskPromFile+" not found";
errReadDisk="Error reading "+diskPromFile;
errNoDiskBuffer="Not enough memory for disk";
TYPE
Byte=[0..255]; Set=SET OF [0..7]; SignedByte=[-128..127];
Word=[0..65535];
Address=Word;
(*
StatusBits=(carry,zero,irqDisable,decimal,break,unused,overflow,negative);
Status=SET OF StatusBits;
*)
Register=RECORD
pc:Address;
a,x,y,sp:Byte;
(* p:Status; *)
END;
(*
The following contain information related to
the disk drives. They currently do not accuratly
reproduce the reald drives.
*)
CONST
minTrack=0; maxTrack=34;
TYPE
DataIndex=[0..250*1024-1];
DiskState=(noDisk,normalDisk,protectedDisk);
DiskBuffer=ARRAY DataIndex OF Byte;
DiskInfo=RECORD
state:DiskState;
motorOn,q6,q7:BOOLEAN;
latch:SHORTCARD;
step:[0..3];
currentTrack:[minTrack..maxTrack];
trackStart,trackEnd,trackPos:DataIndex;
data:POINTER TO DiskBuffer;
lock:e.SignalSemaphore;
END;
ControllerInfo=RECORD
currentDrive:[1..2];
disk:ARRAY [1..2] OF DiskInfo;
END;
CONST
hi=0100H;
(*
Some special memory locations:
*)
CONST
ramStart=0; ramLength=0C000H; ramEnd=ramStart+ramLength-1;
stackBase=0100H;
text1Start=0400H; text1Length=0400H; text1End=text1Start+text1Length-1;
io0Start=0C000H; io0Page=io0Start DIV hi;
promStart=0C600H; promLength=00100H; promEnd=promStart+promLength-1;
romStart=0D000H; romLength=03000H; romEnd=romStart+romLength-1;
nmiVector=0FFFAH;
resetVector=0FFFCH;
irqVector=0FFFEH;
VAR
reg:Register; (* The 6502 registers *)
carry,zero,irqDisable,decimal,break(*,unused*),overflow,negative:BOOLEAN;
operand:Byte; (* Memory operand *)
jumpAddr:Address; (* Jump address *)
memAddr:Address; (* Address of memory reference *)
nextPC,nextPC2:PROC;
clockCount:LONGCARD;
controller:ControllerInfo;
doReset,quit:BOOLEAN;
ram:ARRAY [ramStart..ramEnd] OF Byte; (* The apple's ram *)
rom:ARRAY [romStart..romEnd] OF Byte; (* The apple's rom *)
prom:ARRAY [promStart..promEnd] OF Byte; (* Disk prom *)
(*
Initialise processor state.
*)
PROCEDURE SetRST;
BEGIN
doReset:=TRUE;
END SetRST;
PROCEDURE SetQuit;
BEGIN
quit:=TRUE;
END SetQuit;
PROCEDURE DiskUnload(num:AS.DiskNum);
BEGIN
(*
I usually don't like WITH statements, but here they help
reduce typing, and are not so dangerous.
*)
WITH controller.disk[num] DO
(* Critical section *)
(*!! E.ObtainSemaphore(ADR(lock)); !!*)
state:=noDisk;
trackStart:=0; trackEnd:=0; trackPos:=0;
IF data#NIL THEN Heap.Deallocate(data); data:=NIL; END;
(*!! E.ReleaseSemaphore(ADR(lock)); !!*)
END;
END DiskUnload;
PROCEDURE DiskLoad(num:AS.DiskNum);
VAR
dummy:LONGINT;
f:d.FileHandlePtr;
i:[0..34];
name:ARRAY [0..99] OF CHAR;
trackLength:LONGCARD;
BEGIN
(*
I usually don't like WITH statements, but here they help
reduce typing, and are not so dangerous.
*)
IF AS.RequestDisk(num,name) THEN
WITH controller.disk[num] DO
(* Critical section *)
(*!! E.ObtainSemaphore(ADR(lock)); !!*)
state:=noDisk;
trackStart:=0; trackEnd:=0; trackPos:=0;
IF data=NIL THEN
Heap.Allocate(data,SIZE(DiskBuffer));
N(data,errNoDiskBuffer);
END;
(*!! E.ReleaseSemaphore(ADR(lock)); !!*)
f:=D.Open(ADR(name),d.oldFile);
IF f#NIL THEN
dummy:=D.Read(f,data,SIZE(DiskBuffer));
FOR i:=1 TO currentTrack DO
trackLength:=data^[trackStart]+hi*data^[trackStart+1];
INC(trackStart,trackLength+4);
END;
trackLength:=data^[trackStart]+hi*data^[trackStart+1];
INC(trackStart,2);
trackEnd:=trackStart+trackLength;
trackPos:=trackStart;
state:=normalDisk;
ELSE
Heap.Deallocate(data); data:=NIL;
END;
END;
END;
END DiskLoad;
PROCEDURE DiskProtect(num:AS.DiskNum; protect:AS.Protected);
BEGIN
(*
I usually don't like WITH statements, but here they help
reduce typing, and are not so dangerous.
*)
WITH controller.disk[num] DO
CASE state OF
| noDisk: (* no change *)
| normalDisk: IF protect THEN state:=protectedDisk; END;
| protectedDisk: IF ~protect THEN state:=normalDisk; END;
END;
END;
END DiskProtect;
PROCEDURE RST;
BEGIN
(*
Stack pointer is (mysteriously) decremented by three, but
the stack content is not changed.
No other registers are modified by a reset.
*)
(*$ OverflowChk:=FALSE *)
DEC(reg.sp,3);
(*$ POP OverflowChk *)
reg.pc:=Address(rom[resetVector])+hi*rom[resetVector+1]-1;
(* will be incremented when processor starts. *)
doReset:=FALSE;
END RST;
PROCEDURE InitProcessor;
BEGIN
reg.a:=0; reg.x:=0; reg.y:=0;
reg.sp:=0;
carry:=FALSE; zero:=TRUE; irqDisable:=TRUE; decimal:=FALSE;
break:=TRUE;(* unused:=TRUE;*) overflow:=FALSE; negative:=FALSE;
RST;
END InitProcessor;
(*
Initialise Apple related stuff.
*)
PROCEDURE InitDisk;
BEGIN
controller.currentDrive:=1;
controller.disk[1].state:=noDisk;
controller.disk[1].step:=0;
controller.disk[1].currentTrack:=0;
controller.disk[1].trackStart:=0;
controller.disk[1].trackEnd:=0;
controller.disk[1].trackPos:=0;
controller.disk[1].data:=NIL;
E.InitSemaphore(ADR(controller.disk[1].lock));
controller.disk[2].state:=noDisk;
controller.disk[2].step:=0;
controller.disk[2].currentTrack:=0;
controller.disk[2].trackStart:=0;
controller.disk[2].trackEnd:=0;
controller.disk[2].trackPos:=0;
controller.disk[2].data:=NIL;
E.InitSemaphore(ADR(controller.disk[2].lock));
DiskLoad(1);
END InitDisk;
PROCEDURE InitApple(VAR err:BOOLEAN);
VAR
addr:Address;
col,line:[0..23];
f:d.FileHandlePtr;
BEGIN
err:=FALSE;
FOR line:=0 TO 23 DO
addr:=text1Start+(Address(line) MOD 8)*080H+(line DIV 8)*028H;
AS.PutText(line,0,ADR(ram[addr]),40);
END;
f:=D.Open(ADR(basicRomFile),d.oldFile);
IF f=NIL THEN A(FALSE,errNoBasic); err:=TRUE;
ELSE
IF D.Read(f,ADR(rom[romStart]),romLength)#romLength THEN
A(FALSE,errReadBasic); err:=TRUE;
END;
D.Close(f);
END;
f:=D.Open(ADR(diskPromFile),d.oldFile);
IF f=NIL THEN A(FALSE,errNoDisk); err:=TRUE;
ELSE
IF D.Read(f,ADR(prom[promStart]),promLength)#promLength THEN
A(FALSE,errReadDisk); err:=TRUE;
END;
D.Close(f);
END;
(*
Some patches to reduce boot time, especially during test phase.
*)
rom[0FCA8H]:=0A9H; (* Patch LDA #0, RTS at start of WAIT *)
rom[0FCA9H]:=000H;
rom[0FCAAH]:=060H;
rom[0FBE3H]:=001H; (* Patch to shorten Bell duration to one cycle *)
clockCount:=0;
InitDisk;
END InitApple;
PROCEDURE ADC;
TYPE
XSet=SET OF [0..15];
VAR
a{R.D2},b{R.D3},c:INTEGER;
x{R.D4},y{R.D5},z:INTEGER;
lo:INTEGER;
BEGIN
a:=CAST(SignedByte,reg.a); b:=CAST(SignedByte,operand);
x:=reg.a; y:=operand;
IF carry THEN c:=1; z:=1; ELSE c:=0; z:=0; END;
a:=a+b+c;
overflow:=(a<-128) OR (a>127);
IF decimal THEN
lo:=(x MOD 16)+(y MOD 16)+z;
IF lo>10 THEN lo:=((lo-10) MOD 16)+16; END; (* last +16 is the carry. *)
x:=CAST(INTEGER,CAST(XSet,x)*XSet{4..7})+CAST(INTEGER,CAST(XSet,y)*XSet{4..7})+lo;
carry:=x>9*16+9;
IF carry THEN x:=x-10*16; END;
ELSE
x:=x+y+z;
carry:=x>255;
END;
(*$ OverflowChk:=FALSE RangeChk:=FALSE *)
reg.a:=Byte(x);
(*$ POP OverflowChk POP RangeChk *)
zero:=(a MOD hi)=0;
negative:=ODD(a DIV 128);
END ADC;
PROCEDURE CMP;
VAR
a{R.D2},b{R.D3}:INTEGER;
x{R.D4},y{R.D5}:INTEGER;
lo:INTEGER;
BEGIN
a:=CAST(SignedByte,reg.a); b:=CAST(SignedByte,operand);
x:=reg.a; y:=operand;
a:=a-b;
overflow:=(a<-128) OR (a>127);
x:=x-y;
carry:=x>=0;
(*$ OverflowChk:=FALSE RangeChk:=FALSE *)
a:=Byte(x);
(*$ POP OverflowChk POP RangeChk *)
zero:=a=0;
negative:=a>=128;
END CMP;
PROCEDURE SBC;
TYPE
XSet=SET OF [0..15];
VAR
a{R.D2},b{R.D3},c:INTEGER;
x{R.D4},y{R.D5},z:INTEGER;
lo:INTEGER;
BEGIN
a:=CAST(SignedByte,reg.a); b:=CAST(SignedByte,operand);
x:=reg.a; y:=operand;
IF carry THEN c:=0; z:=0; ELSE c:=1; z:=1; END;
a:=a-b-c;
overflow:=(a<-128) OR (a>127);
IF decimal THEN
HALT;
lo:=(x MOD 16)+(y MOD 16)+z;
IF lo>10 THEN lo:=((lo-10) MOD 16)+16; END; (* last +16 is the carry. *)
x:=CAST(INTEGER,CAST(XSet,x)*XSet{4..7})+CAST(INTEGER,CAST(XSet,y)*XSet{4..7})+lo;
carry:=x>9*16+9;
IF carry THEN x:=x-10*16; END;
ELSE
x:=x-y-z;
carry:=x>=0;
END;
(*$ OverflowChk:=FALSE RangeChk:=FALSE *)
reg.a:=Byte(x);
(*$ POP OverflowChk POP RangeChk *)
zero:=(a MOD hi)=0;
negative:=ODD(a DIV 128);
END SBC;
PROCEDURE CPX;
VAR
a{R.D2},b{R.D3}:INTEGER;
x{R.D4},y{R.D5}:INTEGER;
lo:INTEGER;
BEGIN
a:=CAST(SignedByte,reg.x); b:=CAST(SignedByte,operand);
x:=reg.x; y:=operand;
a:=a-b;
overflow:=(a<-128) OR (a>127);
x:=x-y;
carry:=x>=0;
(*$ OverflowChk:=FALSE RangeChk:=FALSE *)
a:=Byte(x);
(*$ POP OverflowChk POP RangeChk *)
zero:=a=0;
negative:=a>=128;
END CPX;
PROCEDURE CPY;
VAR
a{R.D2},b{R.D3}:INTEGER;
x{R.D4},y{R.D5}:INTEGER;
lo:INTEGER;
BEGIN
a:=CAST(SignedByte,reg.y); b:=CAST(SignedByte,operand);
x:=reg.y; y:=operand;
a:=a-b;
overflow:=(a<-128) OR (a>127);
x:=x-y;
carry:=x>=0;
(*$ OverflowChk:=FALSE RangeChk:=FALSE *)
a:=Byte(x);
(*$ POP OverflowChk POP RangeChk *)
zero:=a=0;
negative:=a>=128;
END CPY;
PROCEDURE ROL;
VAR
oldCarry{R.D2}:Byte;
BEGIN
IF carry THEN oldCarry:=1; ELSE oldCarry:=0; END;
carry:=operand>=128;
(*$ OverflowChk:=FALSE RangeChk:=FALSE *)
operand:=2*operand+oldCarry;
(*$ POP OverflowChk POP RangeChk *)
zero:=operand=0;
negative:=operand>=128;
END ROL;
PROCEDURE ROR;
VAR
oldCarry{R.D2}:Byte;
BEGIN
IF carry THEN oldCarry:=128; ELSE oldCarry:=0; END;
carry:=ODD(operand);
operand:=(operand DIV 2)+oldCarry;
zero:=operand=0;
negative:=operand>=128;
END ROR;
PROCEDURE ASL; BEGIN
carry:=operand>=128;
(*$ OverflowChk:=FALSE RangeChk:=FALSE *)
operand:=2*operand;
(*$ POP OverflowChk POP RangeChk *)
zero:=operand=0;
negative:=operand>=128;
END ASL;
PROCEDURE PHP;
VAR
temp{R.D2}:Byte;
BEGIN
temp:=20H; (* unused is always set *)
IF carry THEN INC(temp,1); END;
IF zero THEN INC(temp,2); END;
IF irqDisable THEN INC(temp,4); END;
IF decimal THEN INC(temp,8); END;
IF break THEN INC(temp,10H); END;
(* unused already set on temp initialization *)
IF overflow THEN INC(temp,40H); END;
IF negative THEN INC(temp,80H); END;
ram[stackBase+reg.sp]:=temp; DEC(reg.sp);
END PHP;
PROCEDURE PLP;
VAR
temp{R.D2}:Byte;
BEGIN
INC(reg.sp); temp:=ram[stackBase+reg.sp];
carry:=ODD(temp);
zero:=ODD(temp DIV 2);
irqDisable:=ODD(temp DIV 4);
decimal:=ODD(temp DIV 8);
(* break and ununsed are not modified *)
overflow:=ODD(temp DIV 40H);
negative:=temp>=80H;
END PLP;
PROCEDURE RTI;
VAR
stack{R.D2}:Address;
temp{R.D3}:Address;
BEGIN
(*$ OverflowChk:=FALSE RangeChk:=FALSE *)
INC(reg.sp); temp:=ram[stackBase+reg.sp];
carry:=ODD(temp);
zero:=ODD(temp DIV 2);
irqDisable:=ODD(temp DIV 4);
decimal:=ODD(temp DIV 8);
(* break and ununsed are not modified *)
overflow:=ODD(temp DIV 40H);
negative:=ODD(temp DIV 80H);
INC(reg.sp); temp:=ram[stackBase+reg.sp];
INC(reg.sp); reg.pc:=temp+hi*ram[stackBase+reg.sp];
(*$ POP OverflowChk POP RangeChk *)
END RTI;
PROCEDURE BRK;
VAR
stack{R.D2}:Address;
temp{R.D3}:Byte;
BEGIN
ram[stackBase+reg.sp]:=reg.pc DIV hi; DEC(reg.sp);
ram[stackBase+reg.sp]:=reg.pc MOD hi; DEC(reg.sp);
temp:=20H; (* unused is always set *)
IF carry THEN INC(temp,1); END;
IF zero THEN INC(temp,2); END;
IF irqDisable THEN INC(temp,4); END;
IF decimal THEN INC(temp,8); END;
IF break THEN INC(temp,10H); END;
(* unused already set on temp initialization *)
IF overflow THEN INC(temp,40H); END;
IF negative THEN INC(temp,80H); END;
ram[stackBase+reg.sp]:=temp; DEC(reg.sp);
reg.pc:=Address(rom[irqVector])+rom[irqVector+1]*hi-1;
(* -1 because it will be incremented before instruction fetch *)
irqDisable:=TRUE; break:=TRUE;
END BRK;
(*
Memory access routines. They have to verify, if special
locations are accessed.
*)
PROCEDURE IORead(addr{R.D7}:Address);
VAR
trackLength:LONGCARD;
BEGIN
CASE addr DIV hi OF
| io0Page:
CASE addr DIV 16 MOD 16 OF
| 00H: operand:=AS.lastKey;
| 01H: IF AS.lastKey>=080H THEN DEC(AS.lastKey,080H); END;
| 0EH: (* Disk *)
CASE addr MOD 16 OF
| 00H:
(*
I usually hate WITH statements, but this would have been too much
writing otherwise.
*)
WITH controller.disk[controller.currentDrive] DO
IF step=3 THEN
(* Critical section *)
(*!! E.ObtainSemaphore(ADR(lock)); !!*)
IF currentTrack<maxTrack THEN
INC(currentTrack);
IF state#noDisk THEN
trackStart:=trackEnd+4;
trackLength:=data^[trackStart-2]+hi*data^[trackStart-1];
trackEnd:=trackStart+trackLength;
trackPos:=trackStart;
END;
END;
(*!! E.ReleaseSemaphore(ADR(lock)); !!*)
step:=0;
ELSIF step=1 THEN
(* Critical section *)
(*!! E.ObtainSemaphore(ADR(lock)); !!*)
IF minTrack<currentTrack THEN
DEC(currentTrack);
IF state#noDisk THEN
trackEnd:=trackStart-4;
trackLength:=data^[trackEnd]+hi*data^[trackEnd+1];
trackStart:=trackEnd-trackLength;
trackPos:=trackStart;
END;
END;
(*!! E.ReleaseSemaphore(ADR(lock)); !!*)
step:=0;
END;
(*$ IF Debug AND Track *)
T.FormatNr("DISK %ld",controller.currentDrive);
T.FormatNr(" T=%2ld",currentTrack);
T.FormatNr("(%ld)\n",step);
(*$ ENDIF *)
END;
| 01H: (* ignore *)
| 02H:
(*
I usually hate WITH statements, but this would have been too much
writing otherwise.
*)
WITH controller.disk[controller.currentDrive] DO
IF (step=0) OR (step=2) THEN step:=1; END;
(*$ IF Debug AND Track *)
T.FormatNr("DISK %ld",controller.currentDrive);
T.FormatNr(" T=%2ld",currentTrack);
T.FormatNr("(%ld)\n",step);
(*$ ENDIF *)
END;
| 03H: (* ignore *)
| 04H:
(*
I usually hate WITH statements, but this would have been too much
writing otherwise.
*)
WITH controller.disk[controller.currentDrive] DO
IF step=1 THEN
(* Critical section *)
(*!! E.ObtainSemaphore(ADR(lock)); !!*)
IF currentTrack<maxTrack THEN
INC(currentTrack);
IF state#noDisk THEN
trackStart:=trackEnd+4;
trackLength:=data^[trackStart-2]+hi*data^[trackStart-1];
trackEnd:=trackStart+trackLength;
trackPos:=trackStart;
END;
END;
(*!! E.ReleaseSemaphore(ADR(lock)); !!*)
step:=2;
ELSIF step=3 THEN
(* Critical section *)
(*!! E.ObtainSemaphore(ADR(lock)); !!*)
IF minTrack<currentTrack THEN
DEC(currentTrack);
IF state#noDisk THEN
trackEnd:=trackStart-4;
trackLength:=data^[trackEnd]+hi*data^[trackEnd+1];
trackStart:=trackEnd-trackLength;
trackPos:=trackStart;
END;
END;
(*!! E.ReleaseSemaphore(ADR(lock)); !!*)
step:=2;
END;
(*$ IF Debug AND Track *)
T.FormatNr("DISK %ld",controller.currentDrive);
T.FormatNr(" T=%2ld",currentTrack);
T.FormatNr("(%ld)\n",step);
(*$ ENDIF *)
END;
| 05H: (* ignore *)
| 06H:
(*
I usually hate WITH statements, but this would have been too much
writing otherwise.
*)
WITH controller.disk[controller.currentDrive] DO
IF (step=2) OR (step=0) THEN step:=3; END;
(*$ IF Debug AND Track *)
T.FormatNr("DISK %ld",controller.currentDrive);
T.FormatNr(" T=%2ld",currentTrack);
T.FormatNr("(%ld)\n",step);
(*$ ENDIF *)
END;
| 07H: (* ignore *)
| 08H:
controller.disk[controller.currentDrive].motorOn:=FALSE;
| 09H:
controller.disk[controller.currentDrive].motorOn:=TRUE;
| 0AH:
IF controller.currentDrive#1 THEN
controller.disk[1].motorOn:=controller.disk[2].motorOn;
controller.disk[2].motorOn:=FALSE;
controller.currentDrive:=1;
END;
| 0BH:
IF controller.currentDrive#2 THEN
controller.disk[2].motorOn:=controller.disk[1].motorOn;
controller.disk[1].motorOn:=FALSE;
controller.currentDrive:=2;
END;
| 0CH:
(*
I usually hate WITH statements, but this would have been too much
writing otherwise.
*)
WITH controller.disk[controller.currentDrive] DO
q6:=FALSE;
(* Critical section *)
(*!! E.ObtainSemaphore(ADR(lock)); !!*)
IF motorOn & (state#noDisk) THEN
IF q7 THEN
IF state=normalDisk THEN data^[trackPos]:=latch; END;
ELSE
operand:=data^[trackPos];
END;
INC(trackPos);
IF trackPos=trackEnd THEN trackPos:=trackStart; END;
(*!! E.ReleaseSemaphore(ADR(lock)); !!*)
ELSE
operand:=0;
END;
END;
| 0DH:
controller.disk[controller.currentDrive].q6:=TRUE;
| 0EH:
(*
I usually hate WITH statements, but this would have been too much
writing otherwise.
*)
WITH controller.disk[controller.currentDrive] DO
q7:=FALSE;
IF motorOn & q6 THEN
IF state=protectedDisk THEN operand:=255; ELSE operand:=0; END;
ELSE
operand:=0;
END;
END;
| 0FH:
controller.disk[controller.currentDrive].q7:=TRUE;
END;
ELSE (* temporarly ignore I/O access *)
END;
| 0C1H..0C5H: (* temporarly I/O access *)
| promStart DIV hi: (* Disk Prom *)
operand:=prom[addr];
| 0C7H..0CFH: (* temporarly I/O access *)
END;
END IORead;
PROCEDURE ReadCheck;
VAR
page{R.D2}:Address;
BEGIN
CASE memAddr DIV hi OF
| (ramStart DIV hi)..(ramEnd DIV hi): operand:=ram[memAddr];
| 0C0H..0CFH: IORead(memAddr);
| (romStart DIV hi)..(romEnd DIV hi): operand:=rom[memAddr];
END;
END ReadCheck;
PROCEDURE NextPC; FORWARD;
PROCEDURE NextRamPC;
VAR
temp{R.D2},page{R.D3}:Address;
BEGIN
temp:=reg.pc+1;
page:=temp DIV hi;
IF ((ramStart DIV hi)<=page) & (page<=(ramEnd DIV hi)) THEN
operand:=ram[temp]; reg.pc:=temp;
ELSE
NextPC;
END;
END NextRamPC;
PROCEDURE NextRomPC;
VAR
temp{R.D2}:Address;
BEGIN
temp:=reg.pc+1;
IF romStart<=temp THEN
operand:=rom[temp]; reg.pc:=temp;
ELSE
NextPC;
END;
END NextRomPC;
PROCEDURE NextPC;
VAR
temp{R.D2}:Address;
BEGIN
temp:=reg.pc+1; reg.pc:=temp;
CASE temp DIV hi OF
| (ramStart DIV hi)..(ramEnd DIV hi): operand:=ram[temp]; nextPC:=NextRamPC;
| io0Page..0CFH: IORead(temp); nextPC:=NextPC;
| (romStart DIV hi)..(romEnd DIV hi): operand:=rom[temp]; nextPC:=NextRomPC;
END;
END NextPC;
PROCEDURE NextPC2; FORWARD;
PROCEDURE NextRamPC2;
VAR
temp{R.D2},page{R.D3},offset{R.D4}:Address;
BEGIN
temp:=reg.pc+1;
offset:=temp MOD hi; page:=temp DIV hi;
IF (offset<0FFH) & ((ramStart DIV hi)<=page) & (page<=(ramEnd DIV hi)) THEN
memAddr:=ram[temp]+hi*ram[temp+1]; reg.pc:=temp+1;
ELSE (* handle the normal way *)
NextPC2;
END;
END NextRamPC2;
PROCEDURE NextRomPC2;
VAR
temp{R.D2},page{R.D3},offset{R.D4}:Address;
BEGIN
temp:=reg.pc+1;
offset:=temp MOD hi; page:=temp DIV hi;
IF (offset<0FFH) & ((romStart DIV hi)<=page) & (page<=(romEnd DIV hi)) THEN
memAddr:=rom[temp]+hi*rom[temp+1]; reg.pc:=temp+1
ELSE (* handle the normal way *)
NextPC2;
END;
END NextRomPC2;
PROCEDURE NextPC2;
VAR
temp{R.D2},page{R.D3},offset{R.D4}:Address;
BEGIN
temp:=reg.pc+1;
offset:=temp MOD hi;
IF offset<0FFH THEN
page:=temp DIV hi;
CASE page OF
| (ramStart DIV hi)..(ramEnd DIV hi):
memAddr:=ram[temp]+hi*ram[temp+1]; nextPC2:=NextRamPC2; reg.pc:=temp+1;
| io0Page..0CFH: (* handle the normal way *)
NextPC; memAddr:=operand;
NextPC; memAddr:=operand*hi+memAddr;
nextPC2:=NextPC2;
| (romStart DIV hi)..(romEnd DIV hi):
memAddr:=rom[temp]+hi*rom[temp+1]; nextPC2:=NextRomPC2; reg.pc:=temp+1;
END;
ELSE (* handle the normal way *)
NextPC; memAddr:=operand;
NextPC; memAddr:=operand*hi+memAddr;
nextPC2:=NextPC2;
END;
END NextPC2;
(*
Memory modification routine. They have to verify if special
locations are addressed.
*)
PROCEDURE WriteCheck;
VAR
col{R.D2},line{R.D3}:Byte;
trackLength:LONGCARD;
BEGIN
CASE memAddr DIV hi OF
| (ramStart DIV hi)..(text1Start DIV hi)-1: ram[memAddr]:=operand;
| (text1Start DIV hi)..(text1End DIV hi):
ram[memAddr]:=operand;
line:=((memAddr-text1Start) DIV 080H)+8*((memAddr-text1Start) MOD 080H DIV 028H);
IF line<24 THEN
col:=(memAddr-text1Start) MOD 080H MOD 028H;
AS.PutText(line,col,ADR(ram[memAddr]),1);
END;
| (text1End DIV hi)+1..(ramEnd DIV hi): ram[memAddr]:=operand;
| io0Page:
CASE memAddr DIV 16 MOD 16 OF
| 00H: (* NOP *)
| 01H: IF AS.lastKey>=080H THEN DEC(AS.lastKey,080H); END;
| 0EH: (* Disk *)
CASE memAddr MOD 16 OF
| 00H:
(*
I usually hate WITH statements, but this would have been too much
writing otherwise.
*)
WITH controller.disk[controller.currentDrive] DO
IF step=3 THEN
(* Critical section *)
(*!! E.ObtainSemaphore(ADR(lock)); !!*)
IF currentTrack<maxTrack THEN
INC(currentTrack);
IF state#noDisk THEN
trackStart:=trackEnd+4;
trackLength:=data^[trackStart-2]+hi*data^[trackStart-1];
trackEnd:=trackStart+trackLength;
trackPos:=trackStart;
END;
END;
(*!! E.ReleaseSemaphore(ADR(lock)); !!*)
step:=0;
ELSIF step=1 THEN
(* Critical section *)
(*!! E.ObtainSemaphore(ADR(lock)); !!*)
IF minTrack<currentTrack THEN
DEC(currentTrack);
IF state#noDisk THEN
trackEnd:=trackStart-4;
trackLength:=data^[trackEnd]+hi*data^[trackEnd+1];
trackStart:=trackEnd-trackLength;
trackPos:=trackStart;
END;
END;
(*!! E.ReleaseSemaphore(ADR(lock)); !!*)
step:=0;
END;
(*$ IF Debug AND Track *)
T.FormatNr("DISK %ld",controller.currentDrive);
T.FormatNr(" T=%2ld",currentTrack);
T.FormatNr("(%ld)\n",step);
(*$ ENDIF *)
END;
| 01H: (* ignore *)
| 02H:
(*
I usually hate WITH statements, but this would have been too much
writing otherwise.
*)
WITH controller.disk[controller.currentDrive] DO
IF (step=0) OR (step=2) THEN step:=1; END;
(*$ IF Debug AND Track *)
T.FormatNr("DISK %ld",controller.currentDrive);
T.FormatNr(" T=%2ld",currentTrack);
T.FormatNr("(%ld)\n",step);
(*$ ENDIF *)
END;
| 03H: (* ignore *)
| 04H:
(*
I usually hate WITH statements, but this would have been too much
writing otherwise.
*)
WITH controller.disk[controller.currentDrive] DO
IF step=1 THEN
(* Critical section *)
(*!! E.ObtainSemaphore(ADR(lock)); !!*)
IF currentTrack<maxTrack THEN
INC(currentTrack);
IF state#noDisk THEN
trackStart:=trackEnd+4;
trackLength:=data^[trackStart-2]+hi*data^[trackStart-1];
trackEnd:=trackStart+trackLength;
trackPos:=trackStart;
END;
END;
(*!! E.ReleaseSemaphore(ADR(lock)); !!*)
step:=2;
ELSIF step=3 THEN
(* Critical section *)
(*!! E.ObtainSemaphore(ADR(lock)); !!*)
IF minTrack<currentTrack THEN
DEC(currentTrack);
IF state#noDisk THEN
trackEnd:=trackStart-4;
trackLength:=data^[trackEnd]+hi*data^[trackEnd+1];
trackStart:=trackEnd-trackLength;
trackPos:=trackStart;
END;
END;
(*!! E.ReleaseSemaphore(ADR(lock)); !!*)
step:=2;
END;
(*$ IF Debug AND Track *)
T.FormatNr("DISK %ld",controller.currentDrive);
T.FormatNr(" T=%2ld",currentTrack);
T.FormatNr("(%ld)\n",step);
(*$ ENDIF *)
END;
| 05H: (* ignore *)
| 06H:
(*
I usually hate WITH statements, but this would have been too much
writing otherwise.
*)
WITH controller.disk[controller.currentDrive] DO
IF (step=2) OR (step=0) THEN step:=3; END;
(*$ IF Debug AND Track *)
T.FormatNr("DISK %ld",controller.currentDrive);
T.FormatNr(" T=%2ld",currentTrack);
T.FormatNr("(%ld)\n",step);
(*$ ENDIF *)
END;
| 07H: (* ignore *)
| 08H:
controller.disk[controller.currentDrive].motorOn:=FALSE;
| 09H:
controller.disk[controller.currentDrive].motorOn:=TRUE;
| 0AH:
IF controller.currentDrive#1 THEN
controller.disk[1].motorOn:=controller.disk[2].motorOn;
controller.disk[2].motorOn:=FALSE;
controller.currentDrive:=1;
END;
| 0BH:
IF controller.currentDrive#2 THEN
controller.disk[2].motorOn:=controller.disk[1].motorOn;
controller.disk[1].motorOn:=FALSE;
controller.currentDrive:=2;
END;
| 0CH:
(*
I usually hate WITH statements, but this would have been too much
writing otherwise.
*)
WITH controller.disk[controller.currentDrive] DO
q6:=FALSE;
(* Critical section *)
(*!! E.ObtainSemaphore(ADR(lock)); !!*)
IF motorOn THEN
IF q7 THEN
data^[trackPos]:=latch; INC(trackPos);
IF trackPos=trackEnd THEN trackPos:=trackStart; END;
END;
END;
(*!! E.ReleaseSemaphore(ADR(lock)); !!*)
END;
| 0DH:
controller.disk[controller.currentDrive].q6:=TRUE;
controller.disk[controller.currentDrive].latch:=operand;
| 0EH:
controller.disk[controller.currentDrive].q7:=FALSE;
| 0FH:
controller.disk[controller.currentDrive].q7:=TRUE;
END;
END;
| (romStart DIV hi)..(romEnd DIV hi): (* NOP *);
END;
END WriteCheck;
(*
The different addressing modes.
*)
PROCEDURE IndJmp;
VAR
temp{R.D2}:Word;
BEGIN
nextPC2;
ReadCheck; jumpAddr:=operand;
INC(memAddr);
ReadCheck; jumpAddr:=jumpAddr+hi*operand;
END IndJmp;
PROCEDURE ZeroR; BEGIN
nextPC;
memAddr:=operand; (* needed by ROL,ROR,ASL,ASR *)
operand:=ram[operand]; (* No ReadCheck needed for zero page *)
END ZeroR;
PROCEDURE IndX;
VAR
temp{R.D2}:Byte;
BEGIN
nextPC;
temp:=(reg.x+operand) MOD hi;
memAddr:=ram[temp]+hi*ram[(temp+1) MOD hi]; (* No ReadCheck needed for zero page *)
(* memAddr has to be checked therefor operand is not modified here *)
END IndX;
PROCEDURE IndY;
VAR
temp{R.D2}:Byte;
BEGIN
nextPC; temp:=operand;
memAddr:=ram[temp]+hi*ram[(temp+1) MOD hi]+reg.y; (* No ReadCheck needed for zero page *)
(* memAddr has to be checked therefor operand is not modified here *)
END IndY;
PROCEDURE ZeroXR; BEGIN
nextPC;
memAddr:=(reg.x+operand) MOD hi; (* needed by DEC,INC,ROL,ROR,ASL,ASR *)
operand:=ram[memAddr]; (* No ReadCheck needed for zero page *)
END ZeroXR;
(*$ OverflowChk:=FALSE RangeChk:=FALSE *)
PROCEDURE ProcessorLoop;
VAR
temp{R.D2}:Byte;
(*$ IF Debug AND Commands *)
tagbuf:ARRAY [0..19] OF LONGCARD;
(*$ ENDIF *)
BEGIN
nextPC:=NextPC; nextPC2:=NextPC2; quit:=FALSE;
LOOP
IF quit THEN B; quit:=FALSE; END;
IF doReset THEN RST; END;
nextPC;
(*$ IF Debug AND Commands *)
IF (clockCount MOD 020000H)<4 THEN (* Dump only a few commands *)
T.Format(
"P=%02lx-%02lx-%02lx-%02lx-%02lx-%02lx-%02lx-%02lx A=%02lx X=%02lx Y=%02lx S=1%02lx PC=%04lx(%02lx)\n",
TAG(tagbuf
,negative,overflow,1,break,decimal,irqDisable,zero,carry,reg.a,reg.x,reg.y,reg.sp,reg.pc,operand
)
);
END;
IF Break.GetBreak()#LONGSET{} THEN B; END;
(*$ ENDIF *)
IF operand<80H THEN
CASE operand OF
| 000H: (* BRK *)
BRK;
INC(clockCount,7);
| 001H: (* ORA (Ind,X) *)
IndX; ReadCheck;
reg.a:=CAST(Byte,CAST(Set,reg.a)+CAST(Set,operand));
zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,6);
| 005H: (* ORA Zero *)
nextPC; operand:=ram[operand];
reg.a:=CAST(Byte,CAST(Set,reg.a)+CAST(Set,operand));
zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,3);
| 006H: (* ASL Zero *)
ZeroR; ASL; ram[memAddr]:=operand;
INC(clockCount,5);
| 008H: (* PHP *)
PHP;
INC(clockCount,3);
| 009H: (* ORA Imm *)
nextPC;
reg.a:=CAST(Byte,CAST(Set,reg.a)+CAST(Set,operand));
zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,2);
| 00AH: (* ASL A *)
operand:=reg.a; ASL; reg.a:=operand;
INC(clockCount,2);
| 00DH: (* ORA Abs *)
nextPC2; ReadCheck;
reg.a:=CAST(Byte,CAST(Set,reg.a)+CAST(Set,operand));
zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,4);
| 00EH: (* ASL Abs *)
nextPC2; ReadCheck; ASL; WriteCheck;
INC(clockCount,6);
| 010H: (* BPL Rel *)
nextPC;
IF ~negative THEN
reg.pc:=CAST(Address,CAST(INTEGER,reg.pc)+CAST(SignedByte,operand));
INC(clockCount,3);
ELSE
INC(clockCount,2);
END;
| 011H: (* ORA (Ind),Y *)
IndY; ReadCheck;
reg.a:=CAST(Byte,CAST(Set,reg.a)+CAST(Set,operand));
zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,5);
| 015H: (* ORA Zero,X *)
nextPC; operand:=ram[(reg.x+operand) MOD hi];
reg.a:=CAST(Byte,CAST(Set,reg.a)+CAST(Set,operand));
zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,4);
| 016H: (* ASL Zero,X *)
ZeroXR; ASL; ram[memAddr]:=operand;
INC(clockCount,6);
| 018H: (* CLC *)
carry:=FALSE;
INC(clockCount,2);
| 019H: (* ORA Abs,Y *)
nextPC2; INC(memAddr,reg.y); ReadCheck;
reg.a:=CAST(Byte,CAST(Set,reg.a)+CAST(Set,operand));
zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,4);
| 01DH: (* ORA Abs,X *)
nextPC2; INC(memAddr,reg.x); ReadCheck;
reg.a:=CAST(Byte,CAST(Set,reg.a)+CAST(Set,operand));
zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,4);
| 01EH: (* ASL Abs,X *)
nextPC2; INC(memAddr,reg.x); ReadCheck; ASL; WriteCheck;
INC(clockCount,7);
| 020H: (* JSR Abs *)
nextPC2;
ram[stackBase+reg.sp]:=reg.pc DIV hi; DEC(reg.sp);
ram[stackBase+reg.sp]:=reg.pc MOD hi; DEC(reg.sp);
reg.pc:=memAddr-1;
INC(clockCount,6);
| 021H: (* AND (Ind,X) *)
IndX; ReadCheck;
reg.a:=CAST(Byte,CAST(Set,reg.a)*CAST(Set,operand));
zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,6);
| 024H: (* BIT Zero *)
nextPC; operand:=ram[operand];
temp:=CAST(Byte,CAST(Set,reg.a)*CAST(Set,operand));
zero:=temp=0; negative:=operand>=128; overflow:=ODD(operand DIV 64);
INC(clockCount,3);
| 025H: (* AND Zero *)
nextPC; operand:=ram[operand];
reg.a:=CAST(Byte,CAST(Set,reg.a)*CAST(Set,operand));
zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,3);
| 026H: (* ROL Zero *)
ZeroR; ROL; ram[memAddr]:=operand;
INC(clockCount,5);
| 028H: (* PLP *)
PLP;
INC(clockCount,3);
| 029H: (* AND Imm *)
nextPC;
reg.a:=CAST(Byte,CAST(Set,reg.a)*CAST(Set,operand));
zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,2);
| 02AH: (* ROL A *)
operand:=reg.a; ROL; reg.a:=operand;
INC(clockCount,2);
| 02CH: (* BIT Abs *)
nextPC2; ReadCheck;
temp:=CAST(Byte,CAST(Set,reg.a)*CAST(Set,operand));
zero:=temp=0; negative:=operand>=128; overflow:=ODD(operand DIV 64);
INC(clockCount,4);
| 02DH: (* AND Abs *)
nextPC2; ReadCheck;
reg.a:=CAST(Byte,CAST(Set,reg.a)*CAST(Set,operand));
zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,4);
| 02EH: (* ROL Abs *)
nextPC2; ReadCheck; ROL; WriteCheck;
INC(clockCount,6);
| 030H: (* BMI Rel *)
nextPC;
IF negative THEN
reg.pc:=CAST(Address,CAST(INTEGER,reg.pc)+CAST(SignedByte,operand));
INC(clockCount,3);
ELSE
INC(clockCount,2);
END;
| 031H: (* AND (Ind),Y *)
IndY; ReadCheck;
reg.a:=CAST(Byte,CAST(Set,reg.a)*CAST(Set,operand));
zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,5);
| 035H: (* AND Zero,X *)
nextPC; operand:=ram[(reg.x+operand) MOD hi];
reg.a:=CAST(Byte,CAST(Set,reg.a)*CAST(Set,operand));
zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,4);
| 036H: (* ROL Zero,X *)
ZeroXR; ROL; ram[memAddr]:=operand;
INC(clockCount,6);
| 038H: (* SEC *)
carry:=TRUE;
INC(clockCount,2);
| 039H: (* AND Abs,Y *)
nextPC2; INC(memAddr,reg.y); ReadCheck;
reg.a:=CAST(Byte,CAST(Set,reg.a)*CAST(Set,operand));
zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,4);
| 03DH: (* AND Abs,X *)
nextPC2; INC(memAddr,reg.x); ReadCheck;
reg.a:=CAST(Byte,CAST(Set,reg.a)*CAST(Set,operand));
zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,4);
| 03EH: (* ROL Abs,X *)
nextPC2; INC(memAddr,reg.x); ReadCheck; ROL; WriteCheck;
INC(clockCount,7);
| 040H: (* RTI *)
RTI;
INC(clockCount,6);
| 041H: (* EOR (Ind,X) *)
IndX; ReadCheck;
reg.a:=CAST(Byte,CAST(Set,reg.a)/CAST(Set,operand));
zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,6);
| 045H: (* EOR Zero *)
nextPC; operand:=ram[operand];
reg.a:=CAST(Byte,CAST(Set,reg.a)/CAST(Set,operand));
zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,3);
| 046H: (* LSR Zero *)
nextPC;
temp:=ram[operand];
carry:=ODD(temp);
temp:=temp DIV 2;
zero:=temp=0; negative:=temp>=128;
ram[operand]:=temp;
INC(clockCount,5);
| 048H: (* PHA *)
ram[stackBase+reg.sp]:=reg.a; DEC(reg.sp);
INC(clockCount,3);
| 049H: (* EOR Imm *)
nextPC;
reg.a:=CAST(Byte,CAST(Set,reg.a)/CAST(Set,operand));
zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,2);
| 04AH: (* LSR A *)
carry:=ODD(reg.a);
reg.a:=reg.a DIV 2;
zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,2);
| 04CH: (* JMP Abs *)
nextPC2; reg.pc:=memAddr-1;
INC(clockCount,3);
| 04DH: (* EOR Abs *)
nextPC2; ReadCheck;
reg.a:=CAST(Byte,CAST(Set,reg.a)/CAST(Set,operand));
zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,4);
| 04EH: (* LSR Abs *)
nextPC2; ReadCheck;
carry:=ODD(operand);
operand:=operand DIV 2;
zero:=operand=0; negative:=operand>=128;
WriteCheck;
INC(clockCount,6);
| 050H: (* BVC Rel *)
nextPC;
IF ~overflow THEN
reg.pc:=CAST(Address,CAST(INTEGER,reg.pc)+CAST(SignedByte,operand));
INC(clockCount,3);
ELSE
INC(clockCount,2);
END;
| 051H: (* EOR (Ind),Y *)
IndY; ReadCheck;
reg.a:=CAST(Byte,CAST(Set,reg.a)/CAST(Set,operand));
zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,5);
| 055H: (* EOR Zero,X *)
nextPC; operand:=ram[(reg.x+operand) MOD hi];
reg.a:=CAST(Byte,CAST(Set,reg.a)/CAST(Set,operand));
zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,4);
| 056H: (* LSR Zero,X *)
ZeroXR;
carry:=ODD(operand);
operand:=operand DIV 2;
zero:=operand=0; negative:=operand>=128;
ram[memAddr]:=operand;
INC(clockCount,6);
| 058H: (* CLI *)
irqDisable:=FALSE;
INC(clockCount,2);
| 059H: (* EOR Abs,Y *)
nextPC2; INC(memAddr,reg.y); ReadCheck;
reg.a:=CAST(Byte,CAST(Set,reg.a)/CAST(Set,operand));
zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,4);
| 05DH: (* EOR Abs,X *)
nextPC2; INC(memAddr,reg.x); ReadCheck;
reg.a:=CAST(Byte,CAST(Set,reg.a)/CAST(Set,operand));
zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,4);
| 05EH: (* LSR Abs,X *)
nextPC2; INC(memAddr,reg.x); ReadCheck;
carry:=ODD(operand);
operand:=operand DIV 2;
zero:=operand=0; negative:=operand>=128;
WriteCheck;
INC(clockCount,7);
| 060H: (* RTS *)
INC(reg.sp); temp:=ram[stackBase+reg.sp];
INC(reg.sp); reg.pc:=temp+hi*ram[stackBase+reg.sp];
INC(clockCount,6);
| 061H: (* ADC (Ind,X) *)
IndX; ReadCheck; ADC;
INC(clockCount,6);
| 065H: (* ADC Zero *)
nextPC; operand:=ram[operand]; ADC;
INC(clockCount,3);
| 066H: (* ROR Zero *)
ZeroR; ROR; ram[memAddr]:=operand;
INC(clockCount,5);
| 068H: (* PLA *)
INC(reg.sp); reg.a:=ram[stackBase+reg.sp];
zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,4);
| 069H: (* ADC Imm *)
nextPC; ADC;
INC(clockCount,2);
| 06AH: (* ROR A *)
operand:=reg.a; ROR; reg.a:=operand;
INC(clockCount,2);
| 06CH: (* JMP (Ind) *)
IndJmp; reg.pc:=jumpAddr-1;
INC(clockCount,5);
| 06DH: (* ADC Abs *)
nextPC2; ReadCheck; ADC;
INC(clockCount,4);
| 06EH: (* ROR Abs *)
nextPC2; ReadCheck; ROR; WriteCheck;
INC(clockCount,6);
| 070H: (* BVS Rel *)
nextPC;
IF overflow THEN
reg.pc:=CAST(Address,CAST(INTEGER,reg.pc)+CAST(SignedByte,operand));
INC(clockCount,3);
ELSE
INC(clockCount,2);
END;
| 071H: (* ADC (Ind),Y *)
IndY; ReadCheck; ADC;
INC(clockCount,5);
| 075H: (* ADC Zero,X *)
nextPC; operand:=ram[(reg.x+operand) MOD hi]; ADC;
INC(clockCount,4);
| 076H: (* ROR Zero,X *)
ZeroXR; ROR; ram[memAddr]:=operand;
INC(clockCount,6);
| 078H: (* STI *)
irqDisable:=TRUE;
INC(clockCount,2);
| 079H: (* ADC Abs,Y *)
nextPC2; INC(memAddr,reg.y); ReadCheck; ADC;
INC(clockCount,4);
| 07DH: (* ADC Abs,X *)
nextPC2; INC(memAddr,reg.x); ReadCheck; ADC;
INC(clockCount,4);
| 07EH: (* ROR Abs,X *)
nextPC2; INC(memAddr,reg.x); ReadCheck; ROR; WriteCheck;
INC(clockCount,7);
ELSE
HALT;
END;
ELSE
CASE operand OF
| 081H: (* STA (Ind,X) *)
IndX; operand:=reg.a; WriteCheck;
INC(clockCount,6);
| 084H: (* STY Zero *)
nextPC; ram[operand]:=reg.y;
INC(clockCount,3);
| 085H: (* STA Zero *)
nextPC; ram[operand]:=reg.a;
INC(clockCount,3);
| 086H: (* STX Zero *)
nextPC; ram[operand]:=reg.x;
INC(clockCount,3);
| 088H: (* DEY *)
DEC(reg.y); zero:=reg.y=0; negative:=reg.y>=128;
INC(clockCount,2);
| 08AH: (* TXA *)
reg.a:=reg.x; zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,2);
| 08CH: (* STY Abs *)
nextPC2; operand:=reg.y; WriteCheck;
INC(clockCount,4);
| 08DH: (* STA Abs *)
nextPC2; operand:=reg.a; WriteCheck;
INC(clockCount,4);
| 08EH: (* STX Abs *)
nextPC2; operand:=reg.x; WriteCheck;
INC(clockCount,4);
| 090H: (* BCC Rel *)
nextPC;
IF ~carry THEN
reg.pc:=CAST(Address,CAST(INTEGER,reg.pc)+CAST(SignedByte,operand));
INC(clockCount,3);
ELSE
INC(clockCount,2);
END;
| 091H: (* STA (Ind),Y *)
IndY; operand:=reg.a; WriteCheck;
INC(clockCount,6);
| 094H: (* STY Zero,X *)
nextPC; ram[(reg.x+operand) MOD hi]:=reg.y;
INC(clockCount,4);
| 095H: (* STA Zero,X *)
nextPC; ram[(reg.x+operand) MOD hi]:=reg.a;
INC(clockCount,4);
| 096H: (* STX Zero,Y *)
nextPC; ram[(reg.y+operand) MOD hi]:=reg.x;
INC(clockCount,4);
| 098H: (* TYA *)
reg.a:=reg.y; zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,2);
| 099H: (* STA Abs,Y *)
nextPC2; INC(memAddr,reg.y); operand:=reg.a; WriteCheck;
INC(clockCount,5);
| 09AH: (* TXS *)
reg.sp:=reg.x;
INC(clockCount,2);
| 09DH: (* STA Abs,X *)
nextPC2; INC(memAddr,reg.x); operand:=reg.a; WriteCheck;
INC(clockCount,5);
| 0A0H: (* LDY Imm *)
nextPC; reg.y:=operand; zero:=reg.y=0; negative:=reg.y>=128;
INC(clockCount,2);
| 0A1H: (* LDA (Ind,X) *)
IndX; ReadCheck; reg.a:=operand; zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,6);
| 0A2H: (* LDX Imm *)
nextPC; reg.x:=operand; zero:=reg.x=0; negative:=reg.x>=128;
INC(clockCount,2);
| 0A4H: (* LDY Zero *)
nextPC; reg.y:=ram[operand]; zero:=reg.y=0; negative:=reg.y>=128;
INC(clockCount,3);
| 0A5H: (* LDA Zero *)
nextPC; reg.a:=ram[operand]; zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,3);
| 0A6H: (* LDX Zero *)
nextPC; reg.x:=ram[operand]; zero:=reg.x=0; negative:=reg.x>=128;
INC(clockCount,3);
| 0A8H: (* TAY *)
reg.y:=reg.a; zero:=reg.y=0; negative:=reg.y>=128;
INC(clockCount,2);
| 0A9H: (* LDA Imm *)
nextPC; reg.a:=operand; zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,2);
| 0AAH: (* TAX *)
reg.x:=reg.a; zero:=reg.x=0; negative:=reg.x>=128;
INC(clockCount,2);
| 0ACH: (* LDY Abs *)
nextPC2; ReadCheck; reg.y:=operand; zero:=reg.y=0; negative:=reg.y>=128;
INC(clockCount,4);
| 0ADH: (* LDA Abs *)
nextPC2; ReadCheck; reg.a:=operand; zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,4);
| 0AEH: (* LDX Abs *)
nextPC2; ReadCheck; reg.x:=operand; zero:=reg.x=0; negative:=reg.x>=128;
INC(clockCount,4);
| 0B0H: (* BCS Rel *)
nextPC;
IF carry THEN
reg.pc:=CAST(Address,CAST(INTEGER,reg.pc)+CAST(SignedByte,operand));
INC(clockCount,3);
ELSE
INC(clockCount,2);
END;
| 0B1H:(* LDA (Ind),Y *)
IndY; ReadCheck; reg.a:=operand; zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,5);
| 0B4H: (* LDY Zero,X *)
nextPC; reg.y:=ram[(reg.x+operand) MOD hi]; zero:=reg.y=0; negative:=reg.y>=128;
INC(clockCount,4);
| 0B5H: (* LDA Zero,X *)
nextPC; reg.a:=ram[(reg.x+operand) MOD hi]; zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,4);
| 0B6H: (* LDX Zero,Y *)
nextPC; reg.x:=ram[(reg.y+operand) MOD hi]; zero:=reg.x=0; negative:=reg.x>=128;
INC(clockCount,4);
| 0B8H: (* CLV *)
overflow:=FALSE;
INC(clockCount,2);
| 0B9H: (* LDA Abs,Y *)
nextPC2; INC(memAddr,reg.y); ReadCheck;
reg.a:=operand; zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,4);
| 0BAH: (* TSX *)
reg.x:=reg.sp; zero:=reg.x=0; negative:=reg.x>=128;
INC(clockCount,2);
| 0BCH: (* LDY Abs,X *)
nextPC2; INC(memAddr,reg.x); ReadCheck;
reg.y:=operand; zero:=reg.y=0; negative:=reg.y>=128;
INC(clockCount,4);
| 0BDH: (* LDA Abs,X *)
nextPC2; INC(memAddr,reg.x); ReadCheck;
reg.a:=operand; zero:=reg.a=0; negative:=reg.a>=128;
INC(clockCount,4);
| 0BEH: (* LDX Abs,Y *)
nextPC2; INC(memAddr,reg.y); ReadCheck;
reg.x:=operand; zero:=reg.x=0; negative:=reg.x>=128;
INC(clockCount,4);
| 0C0H: (* CPY Imm *)
nextPC; CPY;
INC(clockCount,2);
| 0C1H: (* CMP (Ind,X) *)
IndX; ReadCheck; CMP;
INC(clockCount,6);
| 0C4H: (* CPY Zero *)
nextPC; operand:=ram[operand]; CPY;
INC(clockCount,3);
| 0C5H: (* CMP Zero *)
nextPC; operand:=ram[operand]; CMP;
INC(clockCount,3);
| 0C6H: (* DEC Zero *)
nextPC; temp:=ram[operand]; DEC(temp); ram[operand]:=temp;
zero:=temp=0; negative:=temp>=128;
INC(clockCount,5);
| 0C8H: (* INY *)
INC(reg.y); zero:=reg.y=0; negative:=reg.y>=128;
INC(clockCount,2);
| 0C9H: (* CMP Imm *)
nextPC; CMP;
INC(clockCount,2);
| 0CAH: (* DEX *)
DEC(reg.x); zero:=reg.x=0; negative:=reg.x>=128;
INC(clockCount,2);
| 0CCH: (* CPY Abs *)
nextPC2; ReadCheck; CPY;
INC(clockCount,4);
| 0CDH: (* CMP Abs *)
nextPC2; ReadCheck; CMP;
INC(clockCount,4);
| 0CEH: (* DEC Abs *)
nextPC2; ReadCheck;
DEC(operand); zero:=operand=0; negative:=operand>=128;
WriteCheck;
INC(clockCount,6);
| 0D0H: (* BNE Rel *)
nextPC;
IF ~zero THEN
reg.pc:=CAST(Address,CAST(INTEGER,reg.pc)+CAST(SignedByte,operand));
INC(clockCount,3);
ELSE
INC(clockCount,2);
END;
| 0D1H: (* CMP (Ind),Y *)
IndY; ReadCheck; CMP;
INC(clockCount,5);
| 0D5H: (* CMP Zero,X *)
nextPC; operand:=ram[(reg.x+operand) MOD hi]; CMP;
INC(clockCount,4);
| 0D6H: (* DEC Zero,X *)
ZeroXR;
DEC(operand); zero:=operand=0; negative:=operand>=128;
ram[memAddr]:=operand;
INC(clockCount,6);
| 0D8H: (* CLD *)
decimal:=FALSE;
INC(clockCount,2);
| 0D9H: (* CMP Abs,Y *)
nextPC2; INC(memAddr,reg.y); ReadCheck; CMP;
INC(clockCount,4);
| 0DDH: (* CMP Abs,X *)
nextPC2; INC(memAddr,reg.x); ReadCheck; CMP;
INC(clockCount,4);
| 0DEH: (* DEC Abs,X *)
nextPC2; INC(memAddr,reg.x); ReadCheck;
DEC(operand); zero:=operand=0; negative:=operand>=128;
WriteCheck;
INC(clockCount,7);
| 0E0H: (* CPX Imm *)
nextPC; CPX;
INC(clockCount,2);
| 0E1H: (* SBC (Ind,X) *)
IndX; ReadCheck; SBC;
INC(clockCount,6);
| 0E4H: (* CPX Zero *)
nextPC; operand:=ram[operand]; CPX;
INC(clockCount,3);
| 0E5H: (* SBC Zero *)
nextPC; operand:=ram[operand]; SBC;
INC(clockCount,3);
| 0E6H: (* INC Zero *)
nextPC; temp:=ram[operand]; INC(temp); ram[operand]:=temp;
zero:=temp=0; negative:=temp>=128;
INC(clockCount,5);
| 0E8H: (* INX *)
INC(reg.x); zero:=reg.x=0; negative:=reg.x>=128;
INC(clockCount,2);
| 0E9H: (* SBC Imm *)
nextPC; SBC;
INC(clockCount,2);
| 0EAH: (* NOP *)
INC(clockCount,2);
| 0ECH: (* CPX Abs *)
nextPC2; ReadCheck; CPX;
INC(clockCount,4);
| 0EDH: (* SBC Abs *)
nextPC2; ReadCheck; SBC;
INC(clockCount,4);
| 0EEH: (* INC Abs *)
nextPC2; ReadCheck;
INC(operand); zero:=operand=0; negative:=operand>=128;
WriteCheck;
INC(clockCount,6);
| 0F0H: (* BEQ Rel *)
nextPC;
IF zero THEN
reg.pc:=CAST(Address,CAST(INTEGER,reg.pc)+CAST(SignedByte,operand));
INC(clockCount,3);
ELSE
INC(clockCount,2);
END;
| 0F1H: (* SBC (Ind),Y *)
IndY; ReadCheck; SBC;
INC(clockCount,5);
| 0F5H: (* SBC Zero,X *)
nextPC; operand:=ram[(reg.x+operand) MOD hi]; SBC;
INC(clockCount,4);
| 0F6H: (* INC Zero,X *)
ZeroXR;
INC(operand); zero:=operand=0; negative:=operand>=128;
ram[memAddr]:=operand;
INC(clockCount,6);
| 0F8H: (* SED *)
decimal:=TRUE;
INC(clockCount,2);
| 0F9H: (* SBC Abs,Y *)
nextPC2; INC(memAddr,reg.y); ReadCheck; SBC;
INC(clockCount,4);
| 0FDH: (* SBC Abs,X *)
nextPC2; INC(memAddr,reg.x); ReadCheck; SBC;
INC(clockCount,4);
| 0FEH: (* Inc Abs,X *)
nextPC2; INC(memAddr,reg.x); ReadCheck;
INC(operand); zero:=operand=0; negative:=operand>=128;
WriteCheck;
INC(clockCount,7);
ELSE
HALT;
END;
END;
END;
END ProcessorLoop;
(*$ POP OverflowChk POP RangeChk *)
VAR
dummy,oldPri:SHORTINT;
err:BOOLEAN;
task:e.TaskPtr;
BEGIN
(*
lower priority, as this program does not have any waits in it.
*)
task:=E.FindTask(NIL);
oldPri:=E.SetTaskPri(task,4);
dummy:=E.SetTaskPri(task,oldPri-3);
InitApple(err);
IF ~err THEN
InitProcessor;
AS.quit:=SetQuit;
AS.reset:=SetRST;
AS.diskLoad:=DiskLoad;
AS.diskUnload:=DiskUnload;
AS.diskProtect:=DiskProtect;
ProcessorLoop;
END;
CLOSE
(*
Restore tasks priority.
*)
dummy:=E.SetTaskPri(task,oldPri);
END Apple.