P65Utils/P6502utils.pas

1692 lines
59 KiB
ObjectPascal
Raw Permalink Normal View History

2018-08-10 03:04:37 +00:00
{
Description
===========
Utilities for the 6502 CPU. The types here defined are intended to be used for:
* Assembling 6502 instructions.
* Disassembling 6502 instructions.
* Simulating the 6502 execution.
To simulate the CPU, it's assumed there are 64KB of RAM in a virtual system.
The main class TP6502 models a CPU6502 object including access to 64KB RAM.
The aim of this unit is to be used as base for assemblers, compilers and simulators.
Created by Tito Hinostroza 19/05/2018
}
unit P6502utils;
{$mode objfpc}{$H+}
interface
uses
2022-08-14 02:44:13 +00:00
Classes, SysUtils, CPUCore, strutils;
2018-08-10 03:04:37 +00:00
type //Instructions set
TP6502Inst = (
i_ADC, //add with carry
i_AND, //and (with accumulator)
i_ASL, //arithmetic shift left
i_BCC, //branch on carry clear
i_BCS, //branch on carry set
i_BEQ, //branch on equal (zero set)
i_BIT, //bit test
i_BMI, //branch on minus (negative set)
i_BNE, //branch on not equal (zero clear)
i_BPL, //branch on plus (negative clear)
i_BRK, //break / interrupt
i_BVC, //branch on overflow clear
i_BVS, //branch on overflow set
i_CLC, //clear carry
i_CLD, //clear decimal
i_CLI, //clear interrupt disable
i_CLV, //clear overflow
i_CMP, //compare (with accumulator)
i_CPX, //compare with X
i_CPY, //compare with Y
i_DEC, //decrement
i_DEX, //decrement X
i_DEY, //decrement Y
i_EOR, //exclusive or (with accumulator)
i_INC, //increment
i_INX, //increment X
i_INY, //increment Y
i_JMP, //jump
i_JSR, //jump subroutine
i_LDA, //load accumulator
i_LDX, //load X
i_LDY, //load Y
i_LSR, //logical shift right
i_NOP, //no operation
i_ORA, //or with accumulator
i_PHA, //push accumulator
i_PHP, //push processor status (SR)
i_PLA, //pull accumulator
i_PLP, //pull processor status (SR)
i_ROL, //rotate left
i_ROR, //rotate right
i_RTI, //return from interrupt
i_RTS, //return from subroutine
i_SBC, //subtract with carry
i_SEC, //set carry
i_SED, //set decimal
i_SEI, //set interrupt disable
i_STA, //store accumulator
i_STX, //store X
i_STY, //store Y
i_TAX, //transfer accumulator to X
i_TAY, //transfer accumulator to Y
i_TSX, //transfer stack pointer to X
i_TXA, //transfer X to accumulator
i_TXS, //transfer X to stack pointer
i_TYA, //transfer Y to accumulator
//INVALID INSTRUCTION
i_Inval
);
//Addressing Modes
{Implicit Mode and Acumulator Mode are not considered here. We consider
Only Modes with parameters.}
TP6502AddMode = (
aImplicit, //Implied : BRK
aAcumulat, //Acumulator : ASL
aImmediat, //Immediate : ORA #$B2
aAbsolute, //Absolute : JMP $4032
aZeroPage, //Zero page : LDA: $35
aRelative, //Relative : BNE LABEL
aIndirect, //Indirect : JMP ($1000)
aAbsolutX, //Absolute Indexed by X : STA $1000, X
aAbsolutY, //Absolute Indexed by Y : STA $1000, Y
aZeroPagX, //Zero page Indexed by X : LDA $10, X
aZeroPagY, //Zero page Indexed by Y : LDA $10, Y
2018-09-20 20:36:07 +00:00
aIndirecX, //Indexed Indirect: LDA ($40,X) Only for X
aIndirecY //Indirect Indexed: LDA ($40),Y Only for Y
2018-08-10 03:04:37 +00:00
);
TP6502AddModes = set of TP6502AddMode;
//Instruction Information for each Address Mode
TInstructInform = record
2022-03-03 16:04:54 +00:00
Opcode : byte; //Code for instruction
nBytes : byte; //Num. of bytes of the instruction.
Cycles : byte; //Num. of cycles the instruction takes.
optCycles: byte; {Extra options in Num. of cycles:
0 -> No aditional cycles.
1 -> Add 1 to cycles if page boundery is crossed
2 -> Add 1 to cycles if branch occurs on same page.
Add 2 to cycles if branch occurs to different page.
}
2018-08-10 03:04:37 +00:00
end;
{ TP6502Instruct }
2022-03-03 16:04:54 +00:00
//Record for a 6502 instruction
2018-08-10 03:04:37 +00:00
TP6502Instruct = object
public
//Name of the instruction
name: string[3];
//Address Modes supported
addressModes: TP6502AddModes;
//Information for each Address Mode supported
instrInform: array[TP6502AddMode] of TInstructInform;
public
procedure Init(name0: string);
procedure AddAddressMode(aMode: TP6502AddMode; Opcode, nBytes, nCycles: Byte;
2022-03-03 16:04:54 +00:00
optCycles: byte);
function HasOpcode(opc: byte; out adMod: TP6502AddMode): boolean;
2018-08-10 03:04:37 +00:00
end;
const //Constants of address and bit positions for some registers
_C = 0;
2018-09-04 14:11:46 +00:00
_Z = 1;
_D = 3;
_B = 4;
_V = 6;
_N = 7;
2018-08-10 03:04:37 +00:00
// _IRP = 7;
type
2020-02-28 04:48:57 +00:00
{Object representing CPU6502 hardware}
2018-08-10 03:04:37 +00:00
{ TP6502 }
2018-08-10 03:07:26 +00:00
TP6502 = class(TCPUCore)
2020-02-28 04:48:57 +00:00
public //Fields to disassembler instructions
idIns: TP6502Inst; //Instruction ID
modIns: TP6502AddMode; //Address mode
parIns: word; //Instruction parameter. Only valid for some instructions.
private //Fields to process instructions
2018-08-10 03:04:37 +00:00
function GetINTCON: byte;
function GetINTCON_GIE: boolean;
function GetSTATUS_C: boolean;
2018-09-20 20:36:07 +00:00
function GetSTATUS_D: boolean;
2018-09-04 14:11:46 +00:00
function GetSTATUS_N: boolean;
function GetSTATUS_I: boolean;
2018-09-20 20:36:07 +00:00
function GetSTATUS_V: boolean;
2018-08-10 03:04:37 +00:00
function GetSTATUS_Z: boolean;
procedure SetINTCON_GIE(AValue: boolean);
procedure SetSTATUS_C(AValue: boolean);
2018-09-20 20:36:07 +00:00
procedure SetSTATUS_D(AValue: boolean);
2018-09-04 14:11:46 +00:00
procedure SetSTATUS_N(AValue: boolean);
procedure SetSTATUS_I(AValue: boolean);
2018-09-20 20:36:07 +00:00
procedure SetSTATUS_V(AValue: boolean);
2018-08-10 03:04:37 +00:00
procedure SetSTATUS_Z(AValue: boolean);
procedure SetFRAM(value: byte);
function GetFRAM: byte;
2018-09-04 14:11:46 +00:00
public //Fields to modelate internal register (For Simulation)
2020-02-28 04:48:57 +00:00
W : byte; //Work register
X,Y : byte; //Index registers
2018-08-10 03:04:37 +00:00
PC : TWordRec; //PC as record to fast access for bytes
2020-02-28 04:48:57 +00:00
SP : byte; //Stack Pointer
SR : byte; //Status Register
2018-09-04 14:11:46 +00:00
property STATUS: byte read SR;
property STATUS_N: boolean read GetSTATUS_N write SetSTATUS_N;
2018-09-20 20:36:07 +00:00
property STATUS_V: boolean read GetSTATUS_V write SetSTATUS_V;
property STATUS_D: boolean read GetSTATUS_D write SetSTATUS_D;
2018-09-04 14:11:46 +00:00
property STATUS_I: boolean read GetSTATUS_I write SetSTATUS_I;
2018-09-20 20:36:07 +00:00
property STATUS_Z: boolean read GetSTATUS_Z write SetSTATUS_Z;
property STATUS_C: boolean read GetSTATUS_C write SetSTATUS_C;
2018-08-10 03:04:37 +00:00
property INTCON: byte read GetINTCON;
property INTCON_GIE: boolean read GetINTCON_GIE write SetINTCON_GIE;
property FRAM: byte read GetFRAM write SetFRAM;
public //Execution control
function CurInstruction: TP6502Inst;
procedure Exec(aPC: word); override; //Ejecuta la instrucción en la dirección indicada.
procedure Exec; override; //Ejecuta instrucción actual
procedure ExecTo(endAdd: word); override; //Ejecuta hasta cierta dirección
procedure ExecStep; override; //Execute one instruction considering CALL as one instruction
procedure ExecNCycles(nCyc: integer; out stopped: boolean); override; //Ejecuta hasta cierta dirección
2018-08-29 05:14:37 +00:00
procedure Reset(hard: boolean); override;
2018-08-10 03:04:37 +00:00
function ReadPC: dword; override;
procedure WritePC(AValue: dword); override;
public //Memories
2018-08-29 05:14:37 +00:00
procedure Decode(const opCode: word); //Decode instruction.
2018-08-10 03:04:37 +00:00
function DisassemblerAt(addr: word; out nBytesProc: byte; useVarName: boolean
): string; override;
2018-08-29 05:14:37 +00:00
public //RAM memory functions
2019-07-22 05:54:54 +00:00
freeStart: word; //Start address where Free RAM will be searched.
2019-07-17 01:21:12 +00:00
function GetFreeByte(out addr: word): boolean;
function GetFreeBytes(const size: integer; out addr: word): boolean; //obtiene una dirección libre
2018-08-29 05:14:37 +00:00
function TotalMemRAM: integer; //devuelve el total de memoria RAM
2018-08-10 03:04:37 +00:00
function UsedMemRAM: word; //devuelve el total de memoria RAM usada
2018-08-10 03:07:26 +00:00
procedure ExploreUsed(rutExplorRAM: TCPURutExplorRAM); //devuelve un reporte del uso de la RAM
2018-08-10 03:04:37 +00:00
function ValidRAMaddr(addr: word): boolean; //indica si una posición de memoria es válida
2020-02-28 04:48:57 +00:00
public //Methods to code instructions according to syntax
2020-10-23 17:00:07 +00:00
disableCodegen: boolean; //Flag to disable the Code generation.
2022-09-16 14:20:41 +00:00
procedure useRAMCodeOp;
procedure useRAMCodeDa;
2018-09-07 13:50:05 +00:00
procedure codByte(const value: byte; isData: boolean);
2022-09-16 14:20:41 +00:00
procedure codByte(const value: byte; used: TCPURamUsed; name: string = '');
2018-08-29 05:14:37 +00:00
procedure codAsm(const inst: TP6502Inst; addMode: TP6502AddMode; param: word);
2018-09-07 13:50:05 +00:00
procedure cod_JMP_at(iRam0: integer; const k: word);
procedure cod_REL_JMP_at(iRam0: integer; const k: word);
2018-08-10 03:04:37 +00:00
function codInsert(iRam0, nInsert, nWords: integer): boolean;
2018-09-07 13:50:05 +00:00
public //Aditional methods
function IsRelJump(idInst: TP6502Inst): boolean; //Idnetify realtive jumps Opcodes
2019-07-22 05:54:54 +00:00
procedure GenHex(hexFile: string; startAddr: integer = - 1); //genera un archivo hex
2022-09-16 14:20:41 +00:00
function GetASMlineAt(addr: word; incAdrr, incValues, incCom,
incVarNam: boolean; out nBytes: byte): string;
2018-08-29 05:14:37 +00:00
procedure DumpCodeAsm(lOut: TStrings; incAdrr, incValues, incCom,
incVarNam: boolean);
2018-08-10 03:04:37 +00:00
public //Initialization
constructor Create; override;
destructor Destroy; override;
end;
2020-02-28 04:48:57 +00:00
var //Global variables
2022-03-03 16:04:54 +00:00
//Instruction mnemonics.
2018-08-10 03:04:37 +00:00
PIC16InstName: array[low(TP6502Inst)..high(TP6502Inst)] of TP6502Instruct;
2020-10-23 02:48:21 +00:00
function FindOpcode(txt: string; out opCode: TP6502Inst): boolean;
2018-08-10 03:04:37 +00:00
implementation
2020-10-23 02:48:21 +00:00
function FindOpcode(txt: string; out opCode: TP6502Inst): boolean;
{Search a string that represent an instruction (Opcode). If found, returns TRUE and
the instruction identifier in "opCode", otherwise returns FALSE and "i_Inval" in
"opCode". }
var
idInst: TP6502Inst;
tmp: String;
begin
tmp := UpperCase(txt);
for idInst := low(TP6502Inst) to high(TP6502Inst) do begin
if PIC16InstName[idInst].name = tmp then begin
opCode := idInst;
exit(true);
end;
end;
//No found.
opCode := i_Inval;
exit(false);
end;
2018-08-10 03:04:37 +00:00
{ TP6502Instruct }
procedure TP6502Instruct.Init(name0: string);
//Initialize the instruction. Must be called before AddAddressMode().
begin
name := name0; //Set
addressModes:= []; //Clear
end;
procedure TP6502Instruct.AddAddressMode(aMode: TP6502AddMode;
Opcode, nBytes, nCycles: Byte;
2022-03-03 16:04:54 +00:00
optCycles: byte);
2018-08-10 03:04:37 +00:00
{Add a new Address Mode including additional information.
"optCycles" is a flag and indicate aditional considerations on cycles:
2022-03-03 16:04:54 +00:00
0 -> No aditional considerations.
1 -> Add 1 to cycles if page boundery is crossed
2 -> Add 1 to cycles if branch occurs on same page.
Add 2 to cycles if branch occurs to different page.
2018-08-10 03:04:37 +00:00
}
begin
addressModes := addressModes + [aMode]; //Register Mode
//Add information
instrInform[aMode].Opcode:= Opcode;
instrInform[aMode].nBytes:= nBytes;
instrInform[aMode].Cycles:= nCycles;
instrInform[aMode].optCycles := optCycles;
end;
2022-03-03 16:04:54 +00:00
function TP6502Instruct.HasOpcode(opc: byte; out adMod: TP6502AddMode
): boolean;
{Indicates if the Opcode "opc" exits in this instruction. If so, returns TRUE and
the address mode in "adMod".}
begin
for adMod in addressModes do begin //Test for all Address modes supported.
if instrInform[adMod].Opcode = opc then exit(true);
end;
exit(false);
end;
2018-08-10 03:04:37 +00:00
{ TP6502 }
2022-09-16 14:20:41 +00:00
procedure TP6502.useRAMCodeOp;
2020-02-28 04:48:57 +00:00
{Set current position as used and increase the index iRam. If error;update "MsjError"}
2018-08-10 03:04:37 +00:00
begin
2022-09-16 14:20:41 +00:00
ram[iRam].used := ruCodeOp; //Mark as used.
inc(iRam);
end;
procedure TP6502.useRAMCodeDa;
begin
ram[iRam].used := ruCodeDa; //Mark as used.
2018-08-10 03:04:37 +00:00
inc(iRam);
end;
2018-09-07 13:50:05 +00:00
procedure TP6502.codByte(const value: byte; isData: boolean);
{Write a byte to the RAM memory.}
begin
if iRam >= CPUMAXRAM then begin
MsjError := 'RAM Memory limit exceeded.';
exit;
end;
ram[iRam].value := value;
if isData then ram[iRam].name := 'data';
2020-02-28 04:48:57 +00:00
ram[iRam].used := ruData; //Mark as used.
2019-06-13 01:35:28 +00:00
inc(iRam);
2018-09-07 13:50:05 +00:00
end;
2022-09-16 14:20:41 +00:00
procedure TP6502.codByte(const value: byte; used: TCPURamUsed; name: string = '');
{Write a byte to the RAM memory.}
begin
if iRam >= CPUMAXRAM then begin
MsjError := 'RAM Memory limit exceeded.';
exit;
end;
ram[iRam].value := value;
ram[iRam].used := used; //Mark as used.
if name<>'' then ram[iRam].name := name;
inc(iRam);
end;
2018-08-29 05:14:37 +00:00
procedure TP6502.codAsm(const inst: TP6502Inst; addMode: TP6502AddMode; param: word);
2018-09-07 13:50:05 +00:00
{General routine to codify assembler instructions.}
2018-08-10 03:04:37 +00:00
var
rInst: TP6502Instruct;
begin
2020-10-23 17:00:07 +00:00
if disableCodegen then exit; //Test flag
2018-08-10 03:04:37 +00:00
rInst := PIC16InstName[inst];
2018-08-29 05:14:37 +00:00
//Overflow protection
if iRam >= CPUMAXRAM then begin
MsjError := 'RAM Memory limit exceeded.';
exit;
end;
2018-08-10 03:04:37 +00:00
//Write OpCode
2018-09-07 13:50:05 +00:00
if not (addMode in rInst.addressModes) then begin
MsjError := 'Invalid Adrress mode.';
exit;
end;
2018-08-10 03:04:37 +00:00
ram[iRam].value := rInst.instrInform[addMode].Opcode;
2022-09-16 14:20:41 +00:00
useRAMCodeOp; //Set as used and increase index.
2020-02-28 04:48:57 +00:00
//Encode parameters
2018-08-10 03:04:37 +00:00
case addMode of
aImplicit: begin
//No parameters
if addMode in rInst.addressModes then begin
//It's OK
end else begin
MsjError:= 'Invalid Address Mode (Implicit)';
end;
end;
aAcumulat: begin
//No parameters
if addMode in rInst.addressModes then begin
//It's OK
end else begin
MsjError:= 'Invalid Address Mode (Acumulator)';
end;
end;
aImmediat: begin
if addMode in rInst.addressModes then begin
//It's OK
end else begin
MsjError:= 'Invalid Address Mode (Immediate)';
end;
ram[iRam].value := lo(param); //escribe parámetro
2022-09-16 14:20:41 +00:00
useRAMCodeDa;
2018-08-10 03:04:37 +00:00
end;
aAbsolute:begin
ram[iRam].value := lo(param);
2022-09-16 14:20:41 +00:00
useRAMCodeDa;
2018-08-10 03:04:37 +00:00
ram[iRam].value := hi(param);
2022-09-16 14:20:41 +00:00
useRAMCodeDa;
2018-08-10 03:04:37 +00:00
end;
aZeroPage:begin
ram[iRam].value := lo(param);
2022-09-16 14:20:41 +00:00
useRAMCodeDa;
2018-08-10 03:04:37 +00:00
end;
aRelative:begin
ram[iRam].value := lo(param);
2022-09-16 14:20:41 +00:00
useRAMCodeDa;
2018-08-10 03:04:37 +00:00
end;
aIndirect:begin
ram[iRam].value := lo(param);
2022-09-16 14:20:41 +00:00
useRAMCodeDa;
2018-08-10 03:04:37 +00:00
ram[iRam].value := hi(param);
2022-09-16 14:20:41 +00:00
useRAMCodeDa;
2018-08-10 03:04:37 +00:00
end;
aAbsolutX:begin
ram[iRam].value := lo(param);
2022-09-16 14:20:41 +00:00
useRAMCodeDa;
2018-08-10 03:04:37 +00:00
ram[iRam].value := hi(param);
2022-09-16 14:20:41 +00:00
useRAMCodeDa;
2018-08-10 03:04:37 +00:00
end;
aAbsolutY:begin
ram[iRam].value := lo(param);
2022-09-16 14:20:41 +00:00
useRAMCodeDa;
2018-08-10 03:04:37 +00:00
ram[iRam].value := hi(param);
2022-09-16 14:20:41 +00:00
useRAMCodeDa;
2018-08-10 03:04:37 +00:00
end;
aZeroPagX:begin
ram[iRam].value := lo(param);
2022-09-16 14:20:41 +00:00
useRAMCodeDa;
2018-08-10 03:04:37 +00:00
end;
aZeroPagY:begin
ram[iRam].value := lo(param);
2022-09-16 14:20:41 +00:00
useRAMCodeDa;
2018-08-10 03:04:37 +00:00
end;
2018-09-20 20:36:07 +00:00
aIndirecX:begin
2018-08-10 03:04:37 +00:00
ram[iRam].value := lo(param);
2022-09-16 14:20:41 +00:00
useRAMCodeDa;
2018-08-10 03:04:37 +00:00
end;
2018-09-20 20:36:07 +00:00
aIndirecY:begin
2018-08-10 03:04:37 +00:00
ram[iRam].value := lo(param);
2022-09-16 14:20:41 +00:00
useRAMCodeDa;
2018-08-10 03:04:37 +00:00
end;
else
raise Exception.Create('Implementation Error.');
end;
end;
2018-09-07 13:50:05 +00:00
procedure TP6502.cod_JMP_at(iRam0: integer; const k: word);
2018-09-20 20:36:07 +00:00
{Encode the jump address for a jump instruction. Used to complete undefined jumps.}
2018-08-10 18:42:37 +00:00
begin
ram[iRam0+1].value := lo(k);
ram[iRam0+2].value := hi(k);
end;
2018-09-07 13:50:05 +00:00
procedure TP6502.cod_REL_JMP_at(iRam0: integer; const k: word);
2018-09-20 20:36:07 +00:00
{Encode the jump address for a relative jump instruction. Used to complete undefined jumps.}
2018-08-10 18:42:37 +00:00
begin
2018-09-07 13:50:05 +00:00
ram[iRam0+1].value := lo(k);
2018-08-10 18:42:37 +00:00
end;
2018-08-10 03:04:37 +00:00
function TP6502.codInsert(iRam0, nInsert, nWords: integer): boolean;
{Inserta en la posición iRam0, "nInsert" palabras, desplazando "nWords" palabras.
Al final debe quedar "nInsert" palabras de espacio libre en iRam0.
Si hay error devuelve FALSE.}
var
i: Integer;
begin
Result := True; //By default
if iRam+nInsert+nWords-1> CPUMAXRAM then begin
//Overflow on address
exit(false);
end;
for i:= iRam + nInsert + nWords -1 downto iRam + nWords do begin
ram[i] := ram[i-nInsert];
end;
end;
2018-09-07 13:50:05 +00:00
function TP6502.IsRelJump(idInst: TP6502Inst): boolean;
{Returns TRUE if the instruction accept the relative address mode}
begin
Result := PIC16InstName[idInst].instrInform[aRelative].Opcode<>0;
end;
2018-08-10 03:04:37 +00:00
//Campos para procesar instrucciones
2018-09-20 20:36:07 +00:00
function TP6502.GetSTATUS_N: boolean;
2018-08-10 03:04:37 +00:00
begin
2018-09-20 20:36:07 +00:00
Result := (SR and %10000000) <> 0;
2018-08-10 03:04:37 +00:00
end;
2018-09-20 20:36:07 +00:00
procedure TP6502.SetSTATUS_N(AValue: boolean);
2018-08-10 03:04:37 +00:00
begin
2018-09-20 20:36:07 +00:00
if AVAlue then SR := SR or %10000000
else SR := SR and %01111111;
2018-08-10 03:04:37 +00:00
end;
2018-09-20 20:36:07 +00:00
function TP6502.GetSTATUS_V: boolean;
2018-08-10 03:04:37 +00:00
begin
2018-09-20 20:36:07 +00:00
Result := (SR and %01000000) <> 0;
2018-08-10 03:04:37 +00:00
end;
2018-09-20 20:36:07 +00:00
procedure TP6502.SetSTATUS_V(AValue: boolean);
2018-08-10 03:04:37 +00:00
begin
2018-09-20 20:36:07 +00:00
if AVAlue then SR := SR or %01000000
else SR := SR and %10111111;
2018-08-10 03:04:37 +00:00
end;
2018-09-20 20:36:07 +00:00
function TP6502.GetSTATUS_D: boolean;
2018-08-10 03:04:37 +00:00
begin
2018-09-20 20:36:07 +00:00
Result := (SR and %00001000) <> 0;
2018-08-10 03:04:37 +00:00
end;
2018-09-20 20:36:07 +00:00
procedure TP6502.SetSTATUS_D(AValue: boolean);
2018-08-10 03:04:37 +00:00
begin
2018-09-20 20:36:07 +00:00
if AVAlue then SR := SR or %00001000
else SR := SR and %11110111;
2018-08-10 03:04:37 +00:00
end;
2018-09-04 14:11:46 +00:00
function TP6502.GetSTATUS_I: boolean;
2018-08-10 03:04:37 +00:00
begin
2018-09-04 14:11:46 +00:00
Result := (SR and %00000100) <> 0;
2018-08-10 03:04:37 +00:00
end;
2018-09-04 14:11:46 +00:00
procedure TP6502.SetSTATUS_I(AValue: boolean);
2018-08-10 03:04:37 +00:00
begin
2018-09-04 14:11:46 +00:00
if AVAlue then SR := SR or %00000100
else SR := SR and %11111011;
2018-08-10 03:04:37 +00:00
end;
2018-09-20 20:36:07 +00:00
function TP6502.GetSTATUS_Z: boolean;
begin
Result := (SR and %00000010) <> 0;
end;
procedure TP6502.SetSTATUS_Z(AValue: boolean);
begin
if AVAlue then SR := SR or %00000010
else SR := SR and %11111101;
end;
function TP6502.GetSTATUS_C: boolean;
begin
Result := (SR and %00000001) <> 0;
end;
procedure TP6502.SetSTATUS_C(AValue: boolean);
begin
if AVAlue then SR := SR or %00000001
else SR := SR and %11111110;
end;
2018-08-10 03:04:37 +00:00
function TP6502.GetINTCON: byte;
begin
Result := ram[$0B].dvalue;
end;
function TP6502.GetINTCON_GIE: boolean;
begin
Result := (ram[$0B].dvalue and %10000000) <> 0;
end;
procedure TP6502.SetINTCON_GIE(AValue: boolean);
begin
if AVAlue then ram[$0B].dvalue := ram[$0B].dvalue or %10000000
else ram[$0B].dvalue := ram[$0B].dvalue and %01111111;
end;
procedure TP6502.SetFRAM(value: byte);
{Escribe en la RAM; en la dirección global f_, el valor "value"
Para determinar el valor real de la dirección, se toma en cuenta los bits de STATUS}
begin
ram[parIns].value := value;
end;
function TP6502.GetFRAM: byte;
{Devuelve el valor de la RAM, de la posición global f_.
Para determinar el valor real de la dirección, se toma en cuenta los bits de STATUS}
begin
Result := ram[parIns].value;
end;
procedure TP6502.Decode(const opCode: word);
{Decode the value of "opCode" and update:
* "idIns" -> Instruction ID
* "modIns" -> Address Mode
2018-08-29 05:14:37 +00:00
If not found, returns "i_Inval" in "idIns".
2018-08-10 03:04:37 +00:00
}
var
i : TP6502Inst;
begin
//Search the Opcode
for i := low(TP6502Inst) to high(TP6502Inst) do begin
2022-03-03 16:04:54 +00:00
if PIC16InstName[i].HasOpcode(opCode, modIns) then begin
idIns := i;
exit;
2018-08-10 03:04:37 +00:00
end;
end;
2018-08-29 05:14:37 +00:00
//Not found
idIns := i_Inval;
2018-08-10 03:04:37 +00:00
end;
2018-08-29 05:14:37 +00:00
function TP6502.DisassemblerAt(addr: word; out nBytesProc: byte;
useVarName: boolean): string;
{Disassembler the instruction located at "addr". If the instruction is multibyte
the intruction length, will be returned in "nBytesProc".
2018-08-10 03:04:37 +00:00
Global variables used: "idIns", "modIns".
"useVarName" -> Flag to use the name of the variable instead of only the address.
Valid only when a variAble name exists in his address.
}
var
nemo: String;
2020-02-28 04:46:25 +00:00
opCode, par1: Byte;
par2: word;
2018-08-10 03:04:37 +00:00
begin
2018-08-29 05:14:37 +00:00
if addr>CPUMAXRAM-1 then exit('');
opCode := ram[addr].value;
//"par1" and "par2" will be used according to the instruction length.
if addr+1>CPUMAXRAM-1 then exit('');
par1 := ram[addr+1].value;
2018-08-10 03:04:37 +00:00
Decode(opCode); //Decode instruction. Update: "idIns", "modIns".
nemo := trim(PIC16InstName[idIns].name) + ' ';
case modIns of
aImplicit: begin
nBytesProc := 1; //No parameters needed
2018-08-29 05:14:37 +00:00
Result := nemo;
2018-08-10 03:04:37 +00:00
end;
aAcumulat: begin
nBytesProc := 1; //No parameters needed
2018-08-29 05:14:37 +00:00
Result := nemo;
2018-08-10 03:04:37 +00:00
end;
aImmediat: begin
Result := nemo + '#$'+IntToHex(par1,2);
nBytesProc := 2;
end;
aAbsolute: begin
nBytesProc := 3;
2018-08-29 05:14:37 +00:00
if addr+2>CPUMAXRAM-1 then exit('');
2020-02-28 04:46:25 +00:00
par2 := ram[addr+2].value<<8 + par1;
if useVarName and (ram[par2].name<>'') then begin
Result := nemo + ram[par2].name;
end else begin
Result := nemo + '$'+IntToHex(par2, 4);
end;
2018-08-10 03:04:37 +00:00
end;
aZeroPage: begin
2020-02-28 04:46:25 +00:00
if useVarName and (ram[par1].name<>'') then begin
Result := nemo + ram[par1].name;
end else begin
Result := nemo + '$'+IntToHex(par1, 2);
end;
2018-08-10 03:04:37 +00:00
nBytesProc := 2;
end;
aRelative: begin
nBytesProc := 2;
2018-08-29 05:14:37 +00:00
if par1<128 then begin //Positive
Result := nemo + '$'+IntToHex((addr + par1+2) and $FFFF, 4);
end else begin
Result := nemo + '$'+IntToHex((addr + par1-254) and $FFFF, 4);
end;
//Result := nemo + '$'+IntToHex(par1, 2);
2018-08-10 03:04:37 +00:00
end;
aIndirect: begin
nBytesProc := 3;
2018-08-29 05:14:37 +00:00
if addr+2>CPUMAXRAM-1 then exit('');
par2 := ram[addr+2].value;
Result := nemo + '$('+IntToHex(par1 + par2*256, 4)+')';
2018-08-10 03:04:37 +00:00
end;
aAbsolutX: begin
nBytesProc := 3;
2018-08-29 05:14:37 +00:00
if addr+2>CPUMAXRAM-1 then exit('');
par2 := ram[addr+2].value;
Result := nemo + '$'+IntToHex(par1 + par2*256, 4)+',X';
2018-08-10 03:04:37 +00:00
end;
aAbsolutY: begin
nBytesProc := 3;
2018-08-29 05:14:37 +00:00
if addr+2>CPUMAXRAM-1 then exit('');
par2 := ram[addr+2].value;
Result := nemo + '$'+IntToHex(par1 + par2*256, 4)+',Y';
2018-08-10 03:04:37 +00:00
end;
aZeroPagX: begin
2018-08-29 05:14:37 +00:00
nBytesProc := 2;
2018-08-10 03:04:37 +00:00
Result := nemo + '$'+IntToHex(par1, 2)+',X';
end;
aZeroPagY: begin
2018-08-29 05:14:37 +00:00
nBytesProc := 2;
2018-08-10 03:04:37 +00:00
Result := nemo + '$'+IntToHex(par1, 2)+',Y';
end;
2018-09-20 20:36:07 +00:00
aIndirecX: begin
2018-08-10 03:04:37 +00:00
nBytesProc := 2;
2018-08-29 05:14:37 +00:00
Result := nemo + '$('+IntToHex(par1, 2)+',X)';
2018-08-10 03:04:37 +00:00
end;
2018-09-20 20:36:07 +00:00
aIndirecY: begin
2018-08-29 05:14:37 +00:00
nBytesProc := 2;
2018-08-10 03:04:37 +00:00
Result := nemo + '$('+IntToHex(par1, 2)+'),Y';
end;
end;
end;
function TP6502.CurInstruction: TP6502Inst;
2018-08-29 05:14:37 +00:00
{Return the instruction pointed by PC, in this moment.}
2018-08-10 03:04:37 +00:00
begin
Decode(ram[PC.W].value); //decode instruction
Result := idIns;
end;
procedure TP6502.Exec;
{Execute the current instruction.}
begin
Exec(PC.W);
end;
procedure TP6502.Exec(aPC: word);
{Ejecuta la instrución actual con dirección "pc".
Falta implementar las operaciones, cuando acceden al registro INDF, el Watchdog timer,
los contadores, las interrupciones}
var
opc: byte;
2022-09-16 14:20:41 +00:00
nCycles, nBytes, tmp, off, OP1, OP2: byte;
2018-09-20 20:36:07 +00:00
target , addr: word;
2020-02-28 04:46:25 +00:00
C_tmp: Boolean;
tmpRes: integer;
2018-08-10 03:04:37 +00:00
begin
//Decodifica instrucción
2018-08-29 05:14:37 +00:00
aPC := PC.W;
2018-08-10 03:04:37 +00:00
opc := ram[aPC].value;
Decode(opc); //Decode instruction
nCycles := PIC16InstName[idIns].instrInform[modIns].Cycles;
nBytes := PIC16InstName[idIns].instrInform[modIns].nBytes;
2018-09-20 20:36:07 +00:00
//Get Operand
case modIns of
aImmediat: addr := (aPC+1) and $FFFF;
aZeroPage: addr := ram[aPC+1].value;
aZeroPagX: addr := (ram[aPC+1].value + X) and $FF;
aAbsolute: addr := ram[aPC+1].value + 256*ram[aPC+2].value;
aAbsolutX: addr := (ram[aPC+1].value + 256*ram[aPC+2].value + X) and $FFFF;
aAbsolutY: addr := (ram[aPC+1].value + 256*ram[aPC+2].value + Y) and $FFFF;
aIndirecX: begin
tmp := (ram[aPC+1].value + X) and $FF;
addr := ram[tmp].value + 256*ram[tmp+1].value;
end;
aIndirecY: begin
tmp := ram[aPC+1].value;
addr := ram[tmp].value + 256*ram[tmp+1].value + Y;
end;
end;
//Execute
2018-08-10 03:04:37 +00:00
case idIns of
2020-02-28 04:46:25 +00:00
i_ADC: begin //add with carry
2022-09-16 14:20:41 +00:00
OP1 := W; //Keep operand.
OP2 := ram[addr].value;
2020-02-28 04:46:25 +00:00
if STATUS_C then begin
2022-09-16 14:20:41 +00:00
tmpRes := W + OP2 + 1;
2020-02-28 04:46:25 +00:00
end else begin
2022-09-16 14:20:41 +00:00
tmpRes := W + OP2;
2020-02-28 04:46:25 +00:00
end;
W := tmpRes and $FF;
STATUS_Z := W = 0;
STATUS_N := W > 127;
STATUS_C := tmpRes>255;
2022-09-16 14:20:41 +00:00
STATUS_V := ((OP1 XOR W) AND (OP2 XOR W) AND $80)<>0; //Based on: http://www.righto.com/2012/12/the-6502-overflow-flag-explained.html
2020-02-28 04:46:25 +00:00
end;
i_AND: begin //and (with accumulator)
W := W and ram[addr].value;
STATUS_Z := W = 0;
STATUS_N := W > 127;
end;
i_ASL: begin //arithmetic shift left
if modIns = aAcumulat then tmp := W
else tmp := ram[addr].value;
2019-07-17 01:21:12 +00:00
2020-02-28 04:46:25 +00:00
STATUS_C := (tmp and $80) <> 0; //Read bit 7
2022-09-16 14:20:41 +00:00
tmp := (tmp << 1) and $FF;
2020-02-28 04:46:25 +00:00
STATUS_Z := tmp = 0;
STATUS_N := tmp > 127;
if modIns = aAcumulat then W := tmp
else ram[addr].value := tmp;
end;
i_BCC: begin //branch on carry clear
if not STATUS_C then begin
off := ram[aPC+1].value;
Inc(PC.W, nBytes); //Normal Increase PC
if off>127 then begin
PC.W := (PC.W + off - 256) and $FFFF;
end else begin
PC.W := (PC.W + off) and $FFFF;
end;
2022-09-16 14:20:41 +00:00
Inc(nClck, nCycles + 1); //Extra cycle in branch
2020-02-28 04:46:25 +00:00
exit;
end;
end;
i_BCS: begin //branch on carry set
if STATUS_C then begin
off := ram[aPC+1].value;
Inc(PC.W, nBytes); //Normal Increase PC
if off>127 then begin
PC.w := (PC.W + off - 256) and $FFFF;
end else begin
PC.W := (PC.W + off) and $FFFF;
end;
2022-09-16 14:20:41 +00:00
Inc(nClck, nCycles + 1); //Extra cycle in branch
2020-02-28 04:46:25 +00:00
exit;
end;
end;
i_BNE: begin //branch on not equal (zero clear)
if not STATUS_Z then begin
off := ram[aPC+1].value;
Inc(PC.W, nBytes); //Normal Increase PC
if off>127 then begin
PC.W := (PC.W + off - 256) and $FFFF;
end else begin
PC.W := (PC.W + off) and $FFFF;
end;
2022-09-16 14:20:41 +00:00
Inc(nClck, nCycles + 1); //Extra cycle in branch
2020-02-28 04:46:25 +00:00
exit;
end;
end;
i_BEQ: begin //branch on equal (zero set)
if STATUS_Z then begin
off := ram[aPC+1].value;
Inc(PC.W, nBytes); //Normal Increase PC
if off>127 then begin
PC.w := (PC.W + off - 256) and $FFFF;
end else begin
PC.W := (PC.W + off) and $FFFF;
end;
2022-09-16 14:20:41 +00:00
Inc(nClck, nCycles + 1); //Extra cycle in branch
2020-02-28 04:46:25 +00:00
exit;
end;
end;
i_BIT: begin //bit test
STATUS_N := (ram[addr].value AND $80) <> 0;
STATUS_V := (ram[addr].value AND $40) <> 0;
STATUS_Z := (W and ram[addr].value) <> 0;
end;
i_BPL: begin //branch on plus (negative clear)
if not STATUS_N then begin
off := ram[aPC+1].value;
Inc(PC.W, nBytes); //Normal Increase PC
if off>127 then begin
PC.W := (PC.W + off - 256) and $FFFF;
end else begin
PC.W := (PC.W + off) and $FFFF;
end;
2022-09-16 14:20:41 +00:00
Inc(nClck, nCycles + 1); //Extra cycle in branch
2020-02-28 04:46:25 +00:00
exit;
end;
end;
i_BMI: begin //branch on minus (negative set)
if STATUS_N then begin
off := ram[aPC+1].value;
Inc(PC.W, nBytes); //Normal Increase PC
if off>127 then begin
PC.w := (PC.W + off - 256) and $FFFF;
end else begin
PC.W := (PC.W + off) and $FFFF;
end;
2022-09-16 14:20:41 +00:00
Inc(nClck, nCycles + 1); //Extra cycle in branch
2020-02-28 04:46:25 +00:00
exit;
end;
end;
i_BRK: begin //break / interrupt
ram[$100 + SP].value := PC.L;
if SP = $00 then SP := $FF else dec(SP);
ram[$100 + SP].value := PC.H;
if SP = $00 then SP := $FF else dec(SP);
STATUS_I := true;
ram[$100 + SP].value := SR;
if SP = $00 then SP := $FF else dec(SP);
PC.L := ram[$FFFE].value;
PC.H := ram[$FFFF].value;
end;
i_BVC: begin //branch on overflow clear
if not STATUS_V then begin
off := ram[aPC+1].value;
Inc(PC.W, nBytes); //Normal Increase PC
if off>127 then begin
PC.W := (PC.W + off - 256) and $FFFF;
end else begin
PC.W := (PC.W + off) and $FFFF;
end;
2022-09-16 14:20:41 +00:00
Inc(nClck, nCycles + 1); //Extra cycle in branch
2020-02-28 04:46:25 +00:00
exit;
end;
end;
i_BVS: begin //branch on overflow set
if STATUS_V then begin
off := ram[aPC+1].value;
Inc(PC.W, nBytes); //Normal Increase PC
if off>127 then begin
PC.w := (PC.W + off - 256) and $FFFF;
end else begin
PC.W := (PC.W + off) and $FFFF;
end;
2022-09-16 14:20:41 +00:00
Inc(nClck, nCycles + 1); //Extra cycle in branch
2020-02-28 04:46:25 +00:00
exit;
end;
end;
i_CLC: STATUS_C := false; //clear carry
i_CLD: STATUS_D := false; //clear decimal
i_CLI: STATUS_I := false; //clear interrupt disable
i_CLV: STATUS_V := false; //clear overflow
i_CMP: begin //Compare (with accumulator)
tmp := ram[addr].value;
STATUS_Z := W = tmp;
STATUS_C := W >= tmp;
if W = tmp then begin
STATUS_N := false;
end else begin
STATUS_N := ((W-tmp) and $80) <> 0; //Copy bit 7
end;
end;
i_CPX: begin; //compare with X
tmp := ram[addr].value;
STATUS_Z := X = tmp;
STATUS_C := X >= tmp;
if X = tmp then begin
STATUS_N := false;
end else begin
STATUS_N := ((X-tmp) and $80) <> 0; //Copy bit 7
end;
end;
i_CPY: begin //compare with Y
tmp := ram[addr].value;
STATUS_Z := Y = tmp;
STATUS_C := Y >= tmp;
if Y = tmp then begin
STATUS_N := false;
end else begin
STATUS_N := ((Y-tmp) and $80) <> 0; //Copy bit 7
end;
end;
i_DEC: begin //decrement
tmp := (ram[addr].value - 1) and $FF;
ram[addr].value := tmp;
STATUS_Z := tmp = 0;
STATUS_N := tmp > 127;
end;
i_DEX: begin //decrement X
X := (X - 1) and $FF;
STATUS_Z := X = 0;
STATUS_N := X > 127;
end;
i_DEY: begin //decrement Y
Y := (Y - 1) and $FF;
STATUS_Z := Y = 0;
STATUS_N := Y > 127;
end;
i_EOR: begin //exclusive or (with accumulator)
W := W xor ram[addr].value;
STATUS_Z := W = 0;
STATUS_N := W > 127;
end;
i_INC: begin //increment
tmp := (ram[addr].value + 1) and $FF;
ram[addr].value := tmp;
STATUS_Z := tmp = 0;
STATUS_N := tmp > 127;
end;
i_INX: begin //increment X
X := (X + 1) and $FF;
STATUS_Z := X = 0;
STATUS_N := X > 127;
end;
i_INY: begin //increment Y
Y := (Y + 1) and $FF;
STATUS_Z := Y = 0;
STATUS_N := Y > 127;
end;
2018-08-29 05:14:37 +00:00
i_JMP: begin //jump
case modIns of
aAbsolute : begin
PC.L := ram[aPC+1].value;
PC.H := ram[aPC+2].value;
end;
aIndirect: begin
target := ram[aPC+1].value + 256* ram[aPC+2].value;
PC.L := ram[target+1].value;
PC.H := ram[target+2].value;
end;
end;
//Inc(PC.W, nBytes); //No apply
Inc(nClck, nCycles);
exit;
end;
i_JSR: begin
2022-09-16 14:20:41 +00:00
inc(PC.W, 3); //Next position
2018-08-29 05:14:37 +00:00
//Save return
ram[$100 + SP].value := PC.H;
if SP = $00 then SP := $FF else dec(SP);
ram[$100 + SP].value := PC.L;
if SP = $00 then SP := $FF else dec(SP);
PC.L := ram[aPC+1].value;
PC.H := ram[aPC+2].value;
//Inc(PC.W, nBytes); //No apply
Inc(nClck, nCycles);
exit;
end; //jump subroutine
2022-09-16 14:20:41 +00:00
i_LDA: begin //Load accumulator
2018-09-20 20:36:07 +00:00
W := ram[addr].value;
STATUS_Z := W = 0;
STATUS_N := W > 127;
end;
2022-09-16 14:20:41 +00:00
i_LDX: begin //Load X
2018-09-20 20:36:07 +00:00
X := ram[addr].value;
STATUS_Z := X = 0;
STATUS_N := X > 127;
end;
2022-09-16 14:20:41 +00:00
i_LDY: begin //Load y
2018-09-20 20:36:07 +00:00
Y := ram[addr].value;
STATUS_Z := Y = 0;
STATUS_N := Y > 127;
end;
2020-02-28 04:46:25 +00:00
i_LSR: begin
STATUS_N := false;
if modIns = aAcumulat then tmp := W
else tmp := ram[addr].value;
STATUS_C := (tmp and $01) <> 0;
tmp := tmp >> 1;
STATUS_Z := tmp = 0;
if modIns = aAcumulat then W := tmp
else ram[addr].value := tmp;
end; //logical shift right
i_NOP: ; //no operation
2022-09-16 14:20:41 +00:00
i_ORA: begin //Or with accumulator
2020-02-28 04:46:25 +00:00
W := W or ram[addr].value;
STATUS_Z := W = 0;
STATUS_N := W > 127;
end;
2022-09-16 14:20:41 +00:00
i_PHA: begin //Push accumulator
2018-08-29 05:14:37 +00:00
ram[$100 + SP].value := W;
if SP = $00 then SP := $FF else dec(SP);
end;
2022-09-16 14:20:41 +00:00
i_PHP: begin //Push processor status (SR)
2020-02-28 04:46:25 +00:00
ram[$100 + SP].value := STATUS;
if SP = $00 then SP := $FF else dec(SP);
end;
2022-09-16 14:20:41 +00:00
i_PLA: begin //Pull accumulator
2018-08-29 05:14:37 +00:00
if SP = $FF then SP := $00 else inc(SP);
W := ram[$100 + SP].value;
end;
2022-09-16 14:20:41 +00:00
i_PLP: begin //Pull processor status (SR)
2020-02-28 04:46:25 +00:00
if SP = $FF then SP := $00 else inc(SP);
SR := ram[$100 + SP].value;
end;
2022-09-16 14:20:41 +00:00
i_ROL: begin //Rotate left
2020-02-28 04:46:25 +00:00
STATUS_N := false;
if modIns = aAcumulat then tmp := W
else tmp := ram[addr].value;
C_tmp := STATUS_C;
2020-02-28 04:48:57 +00:00
STATUS_C := (tmp and $80) <> 0; //Get bit 7
2020-02-28 04:46:25 +00:00
tmp := byte(tmp << 1);
if C_tmp then tmp := tmp or $01; //Insert bit 0
STATUS_Z := tmp = 0;
STATUS_N := tmp > 127;
if modIns = aAcumulat then W := tmp
else ram[addr].value := tmp;
end;
2022-09-16 14:20:41 +00:00
i_ROR: begin //Rotate right
2020-02-28 04:46:25 +00:00
STATUS_N := false;
if modIns = aAcumulat then tmp := W
else tmp := ram[addr].value;
C_tmp := STATUS_C; //Save previos C
STATUS_C := (tmp and $01) <> 0; //Get bit 0
tmp := tmp >> 1;
if C_tmp then tmp := tmp or $80; //Insert bit 7
STATUS_Z := tmp = 0;
STATUS_N := tmp > 127;
if modIns = aAcumulat then W := tmp
else ram[addr].value := tmp;
end;
2022-09-16 14:20:41 +00:00
i_RTI: begin //Return from interrupt
2020-02-28 04:46:25 +00:00
if SP = $FF then SP := $00 else inc(SP);
SR := ram[$100 + SP].value;
if SP = $FF then SP := $00 else inc(SP);
PC.L := ram[$100 + SP].value;
if SP = $FF then SP := $00 else inc(SP);
PC.H := ram[$100 + SP].value;
//Inc(PC.W, nBytes); //No apply
Inc(nClck, nCycles);
exit;
end;
2022-09-16 14:20:41 +00:00
i_RTS: begin //Return from subroutine
2018-08-29 05:14:37 +00:00
if SP = $FF then SP := $00 else inc(SP);
PC.L := ram[$100 + SP].value;
if SP = $FF then SP := $00 else inc(SP);
PC.H := ram[$100 + SP].value;
//Inc(PC.W, nBytes); //No apply
Inc(nClck, nCycles);
exit;
end;
2022-09-16 14:20:41 +00:00
i_SBC: begin //Subtract with carry
OP1 := W; //Keep operand.
OP2 := ram[addr].value;
2020-02-28 04:46:25 +00:00
if STATUS_C then begin
2022-09-16 14:20:41 +00:00
tmpRes := W - OP2;
2020-02-28 04:46:25 +00:00
end else begin
2022-09-16 14:20:41 +00:00
tmpRes := W - OP2 - 1;
2020-02-28 04:46:25 +00:00
end;
W := tmpRes and $FF;
STATUS_Z := W = 0;
STATUS_N := W > 127;
2022-09-16 14:20:41 +00:00
STATUS_C := not (tmpRes<0);
STATUS_V := ((OP1 XOR W) AND (OP2 XOR W) AND $80)<>0; //Based on: http://www.righto.com/2012/12/the-6502-overflow-flag-explained.html
2020-02-28 04:46:25 +00:00
end;
2018-09-20 20:36:07 +00:00
i_SEC: STATUS_C := true; //set carry
i_SED: STATUS_D := true; //set decimal
i_SEI: STATUS_I := true; //set interrupt disable
i_STA: begin //store accumulator
ram[addr].value := W;
end;
i_STX: begin //store X
ram[addr].value := X;
end;
i_STY: begin //store Y
ram[addr].value := Y;
end;
i_TAX: begin //transfer accumulator to X
X := W;
STATUS_Z := X = 0;
STATUS_N := X > 127;
end;
i_TAY: begin //transfer accumulator to Y
Y := W;
STATUS_Z := Y = 0;
STATUS_N := Y > 127;
end;
i_TSX: begin //transfer stack pointer to X
X := SP;
STATUS_Z := X = 0;
STATUS_N := X > 127;
end;
2020-02-28 04:46:25 +00:00
i_TXA: begin //transfer X to accumulator
W := X;
STATUS_Z := W = 0;
STATUS_N := W > 127;
2018-08-10 03:04:37 +00:00
end;
2020-02-28 04:46:25 +00:00
i_TXS: begin //transfer X to stack pointer
SP := X;
2018-08-10 03:04:37 +00:00
end;
2020-02-28 04:46:25 +00:00
i_TYA: begin //transfer Y to accumulator
W := Y;
STATUS_Z := W = 0;
STATUS_N := W > 127;
2018-08-10 03:04:37 +00:00
end;
i_Inval: begin
2020-02-28 04:46:25 +00:00
MsjError := 'Invalid Opcode';
end;
2018-08-10 03:04:37 +00:00
end;
//Increase counters
Inc(PC.W, nBytes);
Inc(nClck, nCycles);
end;
procedure TP6502.ExecTo(endAdd: word);
{Ejecuta las instrucciones secuencialmente, desde la instrucción actual, hasta que el
contador del programa, sea igual a la dirección "endAdd".}
begin
//Hace una primera ejecución, sin verificar Breakpoints
2022-09-16 14:20:41 +00:00
if ram[PC.W].used = ruUnused then begin
//Encontró un BreakPoint, sale sin ejecutar esa instrucción
if OnExecutionMsg<>nil then OnExecutionMsg('Stopped. Unused RAM location.');
exit;
end;
2018-08-10 03:04:37 +00:00
Exec(PC.W);
//Ejecuta cíclicamnente
while PC.W <> endAdd do begin
if ram[PC.W].breakPnt then begin
//Encontró un BreakPoint, sale sin ejecutar esa instrucción
if OnExecutionMsg<>nil then OnExecutionMsg('Stopped for breakpoint.');
exit;
end;
2022-09-16 14:20:41 +00:00
if ram[PC.W].used = ruUnused then begin
//Encontró un BreakPoint, sale sin ejecutar esa instrucción
if OnExecutionMsg<>nil then OnExecutionMsg('Stopped. Unused RAM location.');
exit;
end;
2018-08-10 03:04:37 +00:00
//Ejecuta
Exec(PC.W);
//Debe haber una forma de salir si es un lazo infinito
//if (nClck and $800000) = $800000 then begin
//end;
end;
end;
procedure TP6502.ExecStep;
begin
if CurInstruction = i_JSR then begin
ExecTo(PC.W+3); //Ejecuta hasta la sgte. instrucción, salta el JSR
end else begin
Exec;
end;
end;
procedure TP6502.ExecNCycles(nCyc: integer; out stopped: boolean);
{Ejecuta el número de ciclos indicados, o hasta que se produzca alguna condición
externa, que puede ser:
* Se encuentre un Punto de Interrupción.
* Se detecta la señal, de detenerse.
* Se genere algún error en la ejecución.
* Se ejecuta la instrucción i_SLEEP.
la bandera "stopped", indica que se ha detendio la ejecución sin completar la cantidad
de instrucciones requeridas.
Normalmente Se ejecutará el número de ciclos indicados, pero en algunos casos se
ejecutará un ciclo más, debido a que algunas instrucciones toman dos ciclos.}
var
clkEnd: Int64;
_pc: word;
begin
clkEnd := nClck + nCyc; //Valor final del contador
while nClck < clkEnd do begin
_pc := PC.W;
if ram[_pc].breakPnt then begin
//Encontró un BreakPoint, sale sin ejecutar esa instrucción
if OnExecutionMsg<>nil then OnExecutionMsg('Stopped for breakpoint.');
stopped := true;
exit;
end;
2019-06-13 01:35:28 +00:00
if ram[_pc].used = ruUnused then begin
2018-08-10 03:04:37 +00:00
//Encontró un BreakPoint, sale sin ejecutar esa instrucción
if OnExecutionMsg<>nil then OnExecutionMsg('Stopped for executing unused code.');
stopped := true;
exit;
end;
if CommStop then begin
//Se detectó el comando STOP
if OnExecutionMsg<>nil then OnExecutionMsg('Stopped for STOP command.');
stopped := true;
exit;
end;
//Ejecuta
Exec(_pc);
end;
stopped := false;
end;
2018-08-29 05:14:37 +00:00
procedure TP6502.Reset(hard: boolean);
2018-08-10 03:04:37 +00:00
//Reinicia el dipsoitivo
var
i: Integer;
begin
PC.W := 0;
W := 0;
2018-08-29 05:14:37 +00:00
SP := $FF; //Posición inicial del puntero de pila
2018-09-04 14:11:46 +00:00
SR := %00000100; //I -> 1
2018-08-10 03:04:37 +00:00
nClck := 0; //Inicia contador de ciclos
CommStop := false; //Limpia bandera
2018-08-29 05:14:37 +00:00
if hard then begin
//Limpia solamente el valor inicial, no toca los otros campos
for i:=0 to high(ram) do begin
ram[i].dvalue := $00;
end;
2018-08-10 03:04:37 +00:00
end;
end;
function TP6502.ReadPC: dword;
begin
Result := PC.W;
end;
procedure TP6502.WritePC(AValue: dword);
begin
PC.W := AValue;
end;
//Funciones para la memoria RAM
2019-07-17 01:21:12 +00:00
function TP6502.GetFreeByte(out addr: word): boolean;
2018-08-29 05:14:37 +00:00
{Devuelve una dirección libre de la memoria RAM, a partir de la dirección iRam.
2018-08-10 03:04:37 +00:00
"Shared" indica que se marcará el bit como de tipo "Compartido", y se usa para el
caso en que se quiera comaprtir la misma posición para diversos variables.
Si encuentra espacio, devuelve TRUE.}
var
i: Integer;
2018-08-29 05:14:37 +00:00
maxRam: dword;
2018-08-10 03:04:37 +00:00
begin
Result := false; //valor inicial
maxRam := CPUMAXRAM; //posición máxima
2019-07-22 05:54:54 +00:00
//Verifica si hay zona de varaibles
if dataAddr1<>-1 then begin
//Busca en zona especial
for i:=dataAddr1 to dataAddr2 do begin
if (ram[i].state = cs_impleGPR) and (ram[i].used = ruUnused) then begin
//Esta dirección está libre
addr := i;
//Notar que la posición de memoria puede estar mapeada.
exit(true); //indica que encontró espacio
end;
end;
//No found. No more space available.
dataAddr1 := -1; //Deactivate
//Continue in the normal RAM
end;
//Busca en la zona normal
for i:=freeStart to maxRam-1 do begin
2019-07-17 01:21:12 +00:00
if (ram[i].state = cs_impleGPR) and (ram[i].used = ruUnused) then begin
2018-08-10 03:04:37 +00:00
//Esta dirección está libre
addr := i;
//Notar que la posición de memoria puede estar mapeada.
2019-07-22 05:54:54 +00:00
exit(true); //indica que encontró espacio
2018-08-10 03:04:37 +00:00
end;
end;
end;
2019-07-17 01:21:12 +00:00
function TP6502.GetFreeBytes(const size: integer; out addr: word): boolean;
{Returns a free memory address of RAM to locate a block of the specified size (in bytes).
If found returns TRUE. }
2018-08-10 03:04:37 +00:00
var
2019-06-13 01:35:28 +00:00
i: dword;
maxRam: dWord;
2018-08-10 03:04:37 +00:00
begin
Result := false; //valor por defecto
2019-07-17 01:21:12 +00:00
if size=0 then begin
addr := 0;
exit(true);
end;
2018-08-10 03:04:37 +00:00
maxRam := CPUMAXRAM;
2019-07-22 05:54:54 +00:00
//Verifica si hay zona de varaibles
if dataAddr1<>-1 then begin
//Busca en zona especial
for i:=dataAddr1 to dataAddr2 do begin
if HaveConsecRAM(i, size, maxRam) then begin
//Encontró del tamaño buscado
addr := i;
exit(true);
end;
end;
//No found. No more space available.
dataAddr1 := -1; //Deactivate
//Continue in the normal RAM
end;
//Busca en la zona normal
for i:=freeStart to maxRam-1 do begin //verifica 1 a 1, por seguridad
2018-08-10 03:04:37 +00:00
if HaveConsecRAM(i, size, maxRam) then begin
2019-07-22 05:54:54 +00:00
//Encontró del tamaño buscado
2018-08-10 03:04:37 +00:00
addr := i;
2019-07-17 01:21:12 +00:00
exit(true);
2018-08-10 03:04:37 +00:00
end;
end;
end;
2018-08-29 05:14:37 +00:00
function TP6502.TotalMemRAM: integer;
2018-08-10 03:04:37 +00:00
{Devuelve el total de memoria RAM disponible}
var
i: Integer;
begin
Result := 0;
for i := 0 to CPUMAXRAM - 1 do begin
2018-08-29 05:14:37 +00:00
if ram[i].Avail then begin
2018-08-10 03:04:37 +00:00
Result := Result + 1;
end;
end;
end;
function TP6502.UsedMemRAM: word;
{Devuelve el total de memoria RAM usada}
var
i: Integer;
begin
Result := 0;
for i := 0 to CPUMAXRAM - 1 do begin
2019-06-13 01:35:28 +00:00
if ram[i].Avail and (ram[i].used<>ruUnused) then begin
2018-08-10 03:04:37 +00:00
//Notar que "AvailGPR" asegura que no se consideran registros maepados
Result := Result + 1;
end;
end;
end;
2018-08-10 03:07:26 +00:00
procedure TP6502.ExploreUsed(rutExplorRAM: TCPURutExplorRAM);
2018-08-10 03:04:37 +00:00
{Genera un reporte de uso de RAM}
var
i: Integer;
begin
for i := 0 to CPUMAXRAM - 1 do begin
2019-06-13 01:35:28 +00:00
if ram[i].Avail and (ram[i].used<>ruUnused) then begin
2018-08-10 03:07:26 +00:00
rutExplorRAM(i, @ram[i]);
2018-08-10 03:04:37 +00:00
end;
end;
end;
function TP6502.ValidRAMaddr(addr: word): boolean;
2019-07-17 01:21:12 +00:00
{Indica si la dirección indicada es válida dentro del hardware del CPU}
2018-08-10 03:04:37 +00:00
begin
if addr > CPUMAXRAM then exit(false); //excede límite
exit(true);
end;
2022-09-16 14:20:41 +00:00
function TP6502.GetASMlineAt(addr: word;
incAdrr, incValues, incCom, incVarNam: boolean; out nBytes: byte): string;
{Returns a line of text with an ASM instruction.}
var
opCode, comLat: string;
begin
comLat := ram[addr].sideComment;
//Decodifica instrucción
opCode := DisassemblerAt(addr, nBytes, incVarNam); //Instrucción
//Verificas si incluye dirección física
Result := '';
if incAdrr then begin
//Agrega dirección al inicio
Result := '$'+IntToHex(addr,4) + ' ';
end;
if incValues then begin
//Agrega bytes después
if nBytes = 1 then begin
Result := Result + IntToHex(ram[addr].value, 2) + ' ' ;
end else if nBytes = 2 then begin
Result := Result + IntToHex(ram[addr].value, 2) + ' ' +
IntToHex(ram[addr+1].value, 2) + ' ';
end else if nBytes = 3 then begin
Result := Result + IntToHex(ram[addr].value, 2) + ' ' +
IntToHex(ram[addr+1].value, 2) + ' ' +
IntToHex(ram[addr+2].value, 2) + ' ';
end;
end;
Result := Result + opCode;
//Check if there is lateral comment
if incCom then begin
Result := Result + ' ' + comLat;
end;
end;
2018-08-29 05:14:37 +00:00
procedure TP6502.DumpCodeAsm(lOut: TStrings;
incAdrr, incValues, incCom, incVarNam: boolean);
2018-08-10 03:04:37 +00:00
{Desensambla las instrucciones grabadas en el PIC.
2022-09-16 14:20:41 +00:00
Se debe llamar despues de llamar a GenHex(), para que se actualicen las variables
minUsed y maxUsed.}
2018-08-10 03:04:37 +00:00
var
i: Word;
2022-09-16 14:20:41 +00:00
lblLin, comLin, lin: String;
2018-08-10 03:04:37 +00:00
nBytes: byte;
2018-08-10 18:42:37 +00:00
const
SPACEPAD = ' ';
2018-08-10 03:04:37 +00:00
begin
//Se supone que minUsed y maxUsed, ya deben haber sido actualizados.
2018-08-10 18:42:37 +00:00
if incAdrr then begin
lOut.Add(SPACEPAD + ' ORG $' + IntToHex(minUsed, 4));
end else begin
lOut.Add(SPACEPAD + 'ORG $' + IntToHex(minUsed, 4));
end;
2018-08-10 03:04:37 +00:00
i := minUsed;
while i <= maxUsed do begin
2022-09-16 14:20:41 +00:00
//Read label and comments.
lblLin := ram[i].name;
2019-06-13 01:35:28 +00:00
comLin := ram[i].topComment;
2022-09-16 14:20:41 +00:00
//Check RAM position.
2019-07-17 01:21:12 +00:00
if ram[i].used in [ruData, ruAbsData] then begin
2022-09-16 14:20:41 +00:00
//Must be a variable.
2018-08-10 18:42:37 +00:00
if incAdrr then begin
2019-06-13 01:35:28 +00:00
if comLin<>'' then lOut.add(comLin);
2022-09-16 14:20:41 +00:00
lOut.Add( PadRight(lblLin, Length(SPACEPAD)) + '$'+IntToHex(i,4) + ' DB ' +
2019-06-13 01:35:28 +00:00
IntToHEx(ram[i].value,2) );
2018-08-10 18:42:37 +00:00
end else begin
2022-09-16 14:20:41 +00:00
lOut.Add( PadRight(lblLin, Length(SPACEPAD)) + 'DB ' + IntToHEx(ram[i].value,2) );
2018-08-10 18:42:37 +00:00
end;
i := i + 1;
2022-09-16 14:20:41 +00:00
end else begin
//Debe ser código o memoria sin usar.
if lblLin<>'' then lOut.Add(lblLin+':'); //Etiqueta al inicio de línea
//Escribe comentario al inicio de línea
if incCom and (comLin<>'') then begin
lOut.Add(comLin);
2018-08-29 05:14:37 +00:00
end;
2022-09-16 14:20:41 +00:00
lin := GetASMlineAt(i, incAdrr, incValues, incCom, incVarNam, nBytes);
lOut.Add(SPACEPAD + lin);
i := i + nBytes; //Incrementa a siguiente instrucción
2018-08-10 03:04:37 +00:00
end;
end;
end;
2019-07-22 05:54:54 +00:00
procedure TP6502.GenHex(hexFile: string; startAddr: integer = -1);
2018-08-29 05:14:37 +00:00
{Genera el archivo *.hex, a partir de los datos almacenados en la memoria RAM.
Actualiza los campos, minUsed y maxUsed.}
var
i: Integer;
2018-09-04 14:11:46 +00:00
f: file of byte;
2018-08-29 05:14:37 +00:00
begin
2019-07-22 05:54:54 +00:00
hexLines.Clear; //Clear list
if startAddr = -1 then begin
//Find first and last byte used
minUsed := CPUMAXRAM;
maxUsed := 0;
for i := 0 to CPUMAXRAM-1 do begin
//if ram[i].used in [ruCode, ruData] then begin //Changed in versión 0.7.1
2022-09-16 14:20:41 +00:00
if ram[i].used in [ruCodeOp, ruCodeDa] then begin
2019-07-22 05:54:54 +00:00
if i<minUsed then minUsed := i;
if i>maxUsed then maxUsed := i;
end;
end;
end else begin
//Find only last byte used
minUsed := startAddr;
maxUsed := 0;
for i := minUsed to CPUMAXRAM-1 do begin
//if ram[i].used in [ruCode, ruData] then begin //Changed in versión 0.7.1
2022-09-16 14:20:41 +00:00
if ram[i].used in [ruCodeOp, ruCodeDa] then begin
2019-07-22 05:54:54 +00:00
if i>maxUsed then maxUsed := i;
end;
2018-08-29 05:14:37 +00:00
end;
end;
2018-09-04 14:11:46 +00:00
//Genera archivo PRG
AssignFile(f, hexFile);
Rewrite(f);
Write(f, minUsed and $ff);
Write(f, (minUsed >> 8) and $ff);
for i := minUsed to maxUsed do begin //Llena buffer
Write(f, ram[i].value);
end;
close(f);
2018-08-29 05:14:37 +00:00
end;
2018-08-10 03:04:37 +00:00
constructor TP6502.Create;
begin
inherited Create;
//Default hardware settings
2018-08-10 18:42:37 +00:00
Model := '6502';
2018-08-29 05:14:37 +00:00
CPUMAXRAM := $10000; //Máx RAM memory
2018-08-10 03:04:37 +00:00
SetLength(ram, CPUMAXRAM);
//inicia una configuración común
ClearMemRAM;
2019-07-22 05:54:54 +00:00
SetStatRAM($0000, $FFFF, cs_impleGPR);
2018-08-10 03:04:37 +00:00
//Estado inicial
iRam := 0; //posición de inicio
end;
destructor TP6502.Destroy;
begin
inherited Destroy;
end;
procedure InitTables;
begin
///////////////////////////////////////////////////////////////////
////////////////// Set instructions information ///////////////////
// Based on the information from:
// http://www.masswerk.at/6502/6502_instruction_set.html
///////////////////////////////////////////////////////////////////
2022-03-03 16:04:54 +00:00
//////////////////////////////
2018-08-10 03:04:37 +00:00
PIC16InstName[i_ADC].name := 'ADC'; //Add Memory to Accumulator with Carry
2022-03-03 16:04:54 +00:00
PIC16InstName[i_ADC].AddAddressMode(aImmediat,$69,2,2,0);
PIC16InstName[i_ADC].AddAddressMode(aZeroPage,$65,2,3,0);
PIC16InstName[i_ADC].AddAddressMode(aZeroPagX,$75,2,4,0);
PIC16InstName[i_ADC].AddAddressMode(aAbsolute,$6D,3,4,0);
PIC16InstName[i_ADC].AddAddressMode(aAbsolutX,$7D,3,4,1);
PIC16InstName[i_ADC].AddAddressMode(aAbsolutY,$79,3,4,1);
PIC16InstName[i_ADC].AddAddressMode(aIndirecX,$61,2,6,0);
PIC16InstName[i_ADC].AddAddressMode(aIndirecY,$71,2,5,1);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_AND].name := 'AND'; //AND Memory with Accumulator
2022-03-03 16:04:54 +00:00
PIC16InstName[i_AND].AddAddressMode(aImmediat,$29,2,2,0);
PIC16InstName[i_AND].AddAddressMode(aZeroPage,$25,2,3,0);
PIC16InstName[i_AND].AddAddressMode(aZeroPagX,$35,2,4,0);
PIC16InstName[i_AND].AddAddressMode(aAbsolute,$2D,3,4,0);
PIC16InstName[i_AND].AddAddressMode(aAbsolutX,$3D,3,4,1);
PIC16InstName[i_AND].AddAddressMode(aAbsolutY,$39,3,4,1);
PIC16InstName[i_AND].AddAddressMode(aIndirecX,$21,2,6,0);
PIC16InstName[i_AND].AddAddressMode(aIndirecY,$31,2,5,1);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_ASL].name := 'ASL'; //Shift Left One Bit (MemoryorAccumulator)
2022-03-03 16:04:54 +00:00
PIC16InstName[i_ASL].AddAddressMode(aAcumulat,$0A,1,2,0);
PIC16InstName[i_ASL].AddAddressMode(aZeroPage,$06,2,5,0);
PIC16InstName[i_ASL].AddAddressMode(aZeroPagX,$16,2,6,0);
PIC16InstName[i_ASL].AddAddressMode(aAbsolute,$0E,3,6,0);
PIC16InstName[i_ASL].AddAddressMode(aAbsolutX,$1E,3,7,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_BCC].name := 'BCC'; //Branch on Carry Clear
2022-03-03 16:04:54 +00:00
PIC16InstName[i_BCC].AddAddressMode(aRelative,$90,2,2,2);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_BCS].name := 'BCS'; //Branch on Carry Set
2022-03-03 16:04:54 +00:00
PIC16InstName[i_BCS].AddAddressMode(aRelative,$B0,2,2,2);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_BEQ].name := 'BEQ'; //Branch on Result Zero
2022-03-03 16:04:54 +00:00
PIC16InstName[i_BEQ].AddAddressMode(aRelative,$F0,2,2,2);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_BIT].name := 'BIT'; //Test Bits in Memory with Accumulator
2022-03-03 16:04:54 +00:00
PIC16InstName[i_BIT].AddAddressMode(aZeroPage,$24,2,3,0);
PIC16InstName[i_BIT].AddAddressMode(aAbsolute,$2C,3,4,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_BMI].name := 'BMI'; //Branch on Result Minus
2022-03-03 16:04:54 +00:00
PIC16InstName[i_BMI].AddAddressMode(aRelative,$30,2,2,2);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_BNE].name := 'BNE'; //Branch on Result not Zero
2022-03-03 16:04:54 +00:00
PIC16InstName[i_BNE].AddAddressMode(aRelative,$D0,2,2,2);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_BPL].name := 'BPL'; //Branch on Result Plus
2022-03-03 16:04:54 +00:00
PIC16InstName[i_BPL].AddAddressMode(aRelative,$10,2,2,2);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_BRK].name := 'BRK'; //Force Break
2022-03-03 16:04:54 +00:00
PIC16InstName[i_BRK].AddAddressMode(aImplicit,$00,1,7,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_BVC].name := 'BVC'; //Branch on Overflow Clear
2022-03-03 16:04:54 +00:00
PIC16InstName[i_BVC].AddAddressMode(aRelative,$50,2,2,2);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_BVS].name := 'BVS'; //Branch on Overflow Set
2022-03-03 16:04:54 +00:00
PIC16InstName[i_BVS].AddAddressMode(aRelative,$70,2,2,2);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_CLC].name := 'CLC'; //Clear Carry Flag
2022-03-03 16:04:54 +00:00
PIC16InstName[i_CLC].AddAddressMode(aImplicit,$18,1,2,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_CLD].name := 'CLD'; //Clear Decimal Mode
2022-03-03 16:04:54 +00:00
PIC16InstName[i_CLD].AddAddressMode(aImplicit,$D8,1,2,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_CLI].name := 'CLI'; //Clear Interrupt Disable Bit
2022-03-03 16:04:54 +00:00
PIC16InstName[i_CLI].AddAddressMode(aImplicit,$58,1,2,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_CLV].name := 'CLV'; //Clear Overflow Flag
2022-03-03 16:04:54 +00:00
PIC16InstName[i_CLV].AddAddressMode(aImplicit,$B8,1,2,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_CMP].name := 'CMP'; //Compare Memory with Accumulator
2022-03-03 16:04:54 +00:00
PIC16InstName[i_CMP].AddAddressMode(aImmediat,$C9,2,2,0);
PIC16InstName[i_CMP].AddAddressMode(aZeroPage,$C5,2,3,0);
PIC16InstName[i_CMP].AddAddressMode(aZeroPagX,$D5,2,4,0);
PIC16InstName[i_CMP].AddAddressMode(aAbsolute,$CD,3,4,0);
PIC16InstName[i_CMP].AddAddressMode(aAbsolutX,$DD,3,4,1);
PIC16InstName[i_CMP].AddAddressMode(aAbsolutY,$D9,3,4,1);
PIC16InstName[i_CMP].AddAddressMode(aIndirecX,$C1,2,6,0);
PIC16InstName[i_CMP].AddAddressMode(aIndirecY,$D1,2,5,1);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_CPX].name := 'CPX'; //Compare Memory and Index X
2022-03-03 16:04:54 +00:00
PIC16InstName[i_CPX].AddAddressMode(aImmediat,$E0,2,2,0);
PIC16InstName[i_CPX].AddAddressMode(aZeroPage,$E4,2,3,0);
PIC16InstName[i_CPX].AddAddressMode(aAbsolute,$EC,3,4,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_CPY].name := 'CPY'; //Compare Memory and Index Y
2022-03-03 16:04:54 +00:00
PIC16InstName[i_CPY].AddAddressMode(aImmediat,$C0,2,2,0);
PIC16InstName[i_CPY].AddAddressMode(aZeroPage,$C4,2,3,0);
PIC16InstName[i_CPY].AddAddressMode(aAbsolute,$CC,3,4,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_DEC].name := 'DEC'; //Decrement Memory by One
2022-03-03 16:04:54 +00:00
PIC16InstName[i_DEC].AddAddressMode(aZeroPage,$C6,2,5,0);
PIC16InstName[i_DEC].AddAddressMode(aZeroPagX,$D6,2,6,0);
PIC16InstName[i_DEC].AddAddressMode(aAbsolute,$CE,3,3,0);
PIC16InstName[i_DEC].AddAddressMode(aAbsolutX,$DE,3,7,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_DEX].name := 'DEX'; //Decrement Index X by One
2022-03-03 16:04:54 +00:00
PIC16InstName[i_DEX].AddAddressMode(aImplicit,$CA,1,2,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_DEY].name := 'DEY'; //Decrement Index Y by One
2022-03-03 16:04:54 +00:00
PIC16InstName[i_DEY].AddAddressMode(aImplicit,$88,1,2,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_EOR].name := 'EOR'; //Exclusive-OR Memory with Accumulator
2022-03-03 16:04:54 +00:00
PIC16InstName[i_EOR].AddAddressMode(aImmediat,$49,2,2,0);
PIC16InstName[i_EOR].AddAddressMode(aZeroPage,$45,2,3,0);
PIC16InstName[i_EOR].AddAddressMode(aZeroPagX,$55,2,4,0);
PIC16InstName[i_EOR].AddAddressMode(aAbsolute,$4D,3,4,0);
PIC16InstName[i_EOR].AddAddressMode(aAbsolutX,$5D,3,4,1);
PIC16InstName[i_EOR].AddAddressMode(aAbsolutY,$59,3,4,1);
PIC16InstName[i_EOR].AddAddressMode(aIndirecX,$41,2,6,0);
PIC16InstName[i_EOR].AddAddressMode(aIndirecY,$51,2,5,1);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_INC].name := 'INC'; //Increment Memory by One
2022-03-03 16:04:54 +00:00
PIC16InstName[i_INC].AddAddressMode(aZeroPage,$E6,2,5,0);
PIC16InstName[i_INC].AddAddressMode(aZeroPagX,$F6,2,6,0);
PIC16InstName[i_INC].AddAddressMode(aAbsolute,$EE,3,6,0);
PIC16InstName[i_INC].AddAddressMode(aAbsolutX,$FE,3,7,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_INX].name := 'INX'; //Increment Index X by One
2022-03-03 16:04:54 +00:00
PIC16InstName[i_INX].AddAddressMode(aImplicit,$E8,1,2,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_INY].name := 'INY'; //Increment Index Y by One
2022-03-03 16:04:54 +00:00
PIC16InstName[i_INY].AddAddressMode(aImplicit,$C8,1,2,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_JMP].name := 'JMP'; //Jump to New Location
2022-03-03 16:04:54 +00:00
PIC16InstName[i_JMP].AddAddressMode(aAbsolute,$4C,3,3,0);
PIC16InstName[i_JMP].AddAddressMode(aIndirect,$6C,3,5,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_JSR].name := 'JSR'; //Jump to New Location Saving Return Address
2022-03-03 16:04:54 +00:00
PIC16InstName[i_JSR].AddAddressMode(aAbsolute,$20,3,6,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_LDA].name := 'LDA'; //Load Accumulator with Memory
2022-03-03 16:04:54 +00:00
PIC16InstName[i_LDA].AddAddressMode(aImmediat,$A9,2,2,0);
PIC16InstName[i_LDA].AddAddressMode(aZeroPage,$A5,2,3,0);
PIC16InstName[i_LDA].AddAddressMode(aZeroPagX,$B5,2,4,0);
PIC16InstName[i_LDA].AddAddressMode(aAbsolute,$AD,3,4,0);
PIC16InstName[i_LDA].AddAddressMode(aAbsolutX,$BD,3,4,1);
PIC16InstName[i_LDA].AddAddressMode(aAbsolutY,$B9,3,4,1);
PIC16InstName[i_LDA].AddAddressMode(aIndirecX,$A1,2,6,0);
PIC16InstName[i_LDA].AddAddressMode(aIndirecY,$B1,2,5,1);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_LDX].name := 'LDX'; //Load Index X with Memory
2022-03-03 16:04:54 +00:00
PIC16InstName[i_LDX].AddAddressMode(aImmediat,$A2,2,2,0);
PIC16InstName[i_LDX].AddAddressMode(aZeroPage,$A6,2,3,0);
PIC16InstName[i_LDX].AddAddressMode(aZeroPagY,$B6,2,4,0);
PIC16InstName[i_LDX].AddAddressMode(aAbsolute,$AE,3,4,0);
PIC16InstName[i_LDX].AddAddressMode(aAbsolutY,$BE,3,4,1);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_LDY].name := 'LDY'; //Load Index Y with Memory
2022-03-03 16:04:54 +00:00
PIC16InstName[i_LDY].AddAddressMode(aImmediat,$A0,2,2,0);
PIC16InstName[i_LDY].AddAddressMode(aZeroPage,$A4,2,3,0);
PIC16InstName[i_LDY].AddAddressMode(aZeroPagX,$B4,2,4,0);
PIC16InstName[i_LDY].AddAddressMode(aAbsolute,$AC,3,4,0);
PIC16InstName[i_LDY].AddAddressMode(aAbsolutX,$BC,3,4,1);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_LSR].name := 'LSR'; //Shift One Bit Right (Memory orAccumulator)
2022-03-03 16:04:54 +00:00
PIC16InstName[i_LSR].AddAddressMode(aAcumulat,$4A,1,2,0);
PIC16InstName[i_LSR].AddAddressMode(aZeroPage,$46,2,5,0);
PIC16InstName[i_LSR].AddAddressMode(aZeroPagX,$56,2,6,0);
PIC16InstName[i_LSR].AddAddressMode(aAbsolute,$4E,3,6,0);
PIC16InstName[i_LSR].AddAddressMode(aAbsolutX,$5E,3,7,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_NOP].name := 'NOP'; //No Operation
2022-03-03 16:04:54 +00:00
PIC16InstName[i_NOP].AddAddressMode(aImplicit,$EA,1,2,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_ORA].name := 'ORA'; //OR Memory with Accumulator
2022-03-03 16:04:54 +00:00
PIC16InstName[i_ORA].AddAddressMode(aImmediat,$09,2,2,0);
PIC16InstName[i_ORA].AddAddressMode(aZeroPage,$05,2,3,0);
PIC16InstName[i_ORA].AddAddressMode(aZeroPagX,$15,2,4,0);
PIC16InstName[i_ORA].AddAddressMode(aAbsolute,$0D,3,4,0);
PIC16InstName[i_ORA].AddAddressMode(aAbsolutX,$1D,3,4,1);
PIC16InstName[i_ORA].AddAddressMode(aAbsolutY,$19,3,4,1);
PIC16InstName[i_ORA].AddAddressMode(aIndirecX,$01,2,6,0);
PIC16InstName[i_ORA].AddAddressMode(aIndirecY,$11,2,5,1);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_PHA].name := 'PHA'; //Push Accumulator on Stack
2022-03-03 16:04:54 +00:00
PIC16InstName[i_PHA].AddAddressMode(aImplicit,$48,1,3,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_PHP].name := 'PHP'; //Push Processor Status on Stack
2022-03-03 16:04:54 +00:00
PIC16InstName[i_PHP].AddAddressMode(aImplicit,$08,1,3,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_PLA].name := 'PLA'; //Pull Accumulator from Stack
2022-03-03 16:04:54 +00:00
PIC16InstName[i_PLA].AddAddressMode(aImplicit,$68,1,4,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_PLP].name := 'PLP'; //Pull Processor Status fromStack
2022-03-03 16:04:54 +00:00
PIC16InstName[i_PLP].AddAddressMode(aImplicit,$28,1,4,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_ROL].name := 'ROL'; //Rotate One Bit Left (Memory orAccumulator)
2022-03-03 16:04:54 +00:00
PIC16InstName[i_ROL].AddAddressMode(aAcumulat,$2A,1,2,0);
PIC16InstName[i_ROL].AddAddressMode(aZeroPage,$26,2,5,0);
PIC16InstName[i_ROL].AddAddressMode(aZeroPagX,$36,2,6,0);
PIC16InstName[i_ROL].AddAddressMode(aAbsolute,$2E,3,6,0);
PIC16InstName[i_ROL].AddAddressMode(aAbsolutX,$3E,3,7,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_ROR].name := 'ROR'; //Rotate One Bit Right (Memory or Accumulator)
2022-03-03 16:04:54 +00:00
PIC16InstName[i_ROR].AddAddressMode(aAcumulat,$6A,1,2,0);
PIC16InstName[i_ROR].AddAddressMode(aZeroPage,$66,2,5,0);
PIC16InstName[i_ROR].AddAddressMode(aZeroPagX,$76,2,6,0);
PIC16InstName[i_ROR].AddAddressMode(aAbsolute,$6E,3,6,0);
PIC16InstName[i_ROR].AddAddressMode(aAbsolutX,$7E,3,7,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_RTI].name := 'RTI'; //Return from Interrupt
2022-03-03 16:04:54 +00:00
PIC16InstName[i_RTI].AddAddressMode(aImplicit,$40,1,6,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_RTS].name := 'RTS'; //Return from Subroutine
2022-03-03 16:04:54 +00:00
PIC16InstName[i_RTS].AddAddressMode(aImplicit,$60,1,6,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_SBC].name := 'SBC'; //Subtract Memory from Accumulator withBorrow
2022-03-03 16:04:54 +00:00
PIC16InstName[i_SBC].AddAddressMode(aImmediat,$E9,2,2,0);
PIC16InstName[i_SBC].AddAddressMode(aZeroPage,$E5,2,3,0);
PIC16InstName[i_SBC].AddAddressMode(aZeroPagX,$F5,2,4,0);
PIC16InstName[i_SBC].AddAddressMode(aAbsolute,$ED,3,4,0);
PIC16InstName[i_SBC].AddAddressMode(aAbsolutX,$FD,3,4,1);
PIC16InstName[i_SBC].AddAddressMode(aAbsolutY,$F9,3,4,1);
PIC16InstName[i_SBC].AddAddressMode(aIndirecX,$E1,2,6,0);
PIC16InstName[i_SBC].AddAddressMode(aIndirecY,$F1,2,5,1);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_SEC].name := 'SEC'; //Set Carry Flag
2022-03-03 16:04:54 +00:00
PIC16InstName[i_SEC].AddAddressMode(aImplicit,$38,1,2,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_SED].name := 'SED'; //Set Decimal Flag
2022-03-03 16:04:54 +00:00
PIC16InstName[i_SED].AddAddressMode(aImplicit,$F8,1,2,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_SEI].name := 'SEI'; //Set Interrupt Disable Status
2022-03-03 16:04:54 +00:00
PIC16InstName[i_SEI].AddAddressMode(aImplicit,$78,1,2,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_STA].name := 'STA'; //Store Accumulator in Memory
2022-03-03 16:04:54 +00:00
PIC16InstName[i_STA].AddAddressMode(aZeroPage,$85,2,3,0);
PIC16InstName[i_STA].AddAddressMode(aZeroPagX,$95,2,4,0);
PIC16InstName[i_STA].AddAddressMode(aAbsolute,$8D,3,4,0);
PIC16InstName[i_STA].AddAddressMode(aAbsolutX,$9D,3,5,0);
PIC16InstName[i_STA].AddAddressMode(aAbsolutY,$99,3,5,0);
PIC16InstName[i_STA].AddAddressMode(aIndirecX,$81,2,6,0);
PIC16InstName[i_STA].AddAddressMode(aIndirecY,$91,2,6,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_STX].name := 'STX'; //Store Index X in Memory
2022-03-03 16:04:54 +00:00
PIC16InstName[i_STX].AddAddressMode(aZeroPage,$86,2,3,0);
PIC16InstName[i_STX].AddAddressMode(aZeroPagY,$96,2,4,0);
PIC16InstName[i_STX].AddAddressMode(aAbsolute,$8E,3,4,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_STY].name := 'STY'; //Sore Index Y in Memory
2022-03-03 16:04:54 +00:00
PIC16InstName[i_STY].AddAddressMode(aZeroPage,$84,2,3,0);
PIC16InstName[i_STY].AddAddressMode(aZeroPagX,$94,2,4,0);
PIC16InstName[i_STY].AddAddressMode(aAbsolute,$8C,3,4,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_TAX].name := 'TAX'; //Transfer Accumulator to IndexX
2022-03-03 16:04:54 +00:00
PIC16InstName[i_TAX].AddAddressMode(aImplicit,$AA,1,2,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_TAY].name := 'TAY'; //Transfer Accumulator to IndexY
2022-03-03 16:04:54 +00:00
PIC16InstName[i_TAY].AddAddressMode(aImplicit,$A8,1,2,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_TSX].name := 'TSX'; //Transfer Stack Pointer toIndex X
2022-03-03 16:04:54 +00:00
PIC16InstName[i_TSX].AddAddressMode(aImplicit,$BA,1,2,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_TXA].name := 'TXA'; //Transfer Index X to Accumulator
2022-03-03 16:04:54 +00:00
PIC16InstName[i_TXA].AddAddressMode(aImplicit,$8A,1,2,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_TXS].name := 'TXS'; //Transfer Index X to StackRegister
2022-03-03 16:04:54 +00:00
PIC16InstName[i_TXS].AddAddressMode(aImplicit,$9A,1,2,0);
2018-08-10 03:04:37 +00:00
PIC16InstName[i_TYA].name := 'TYA'; //Transfer Index Y to Accumulator
2022-03-03 16:04:54 +00:00
PIC16InstName[i_TYA].AddAddressMode(aImplicit,$98,1,2,0);
2018-08-10 03:04:37 +00:00
2019-07-17 01:21:12 +00:00
PIC16InstName[i_Inval].name := 'Inv';
2018-08-10 03:04:37 +00:00
end;
initialization
InitTables;
end. //1550