{ 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 Classes, SysUtils, LCLProc, PicCore; 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 = ( aImplied_, //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 aIdxIndir, //Indexed Indirect: LDA ($40,X) Only for X aIndirIdx //Indirect Indexed: LDA ($40),Y Only for Y ); TP6502AddModes = set of TP6502AddMode; //Instruction Information for each Address Mode TInstructInform = record Opcode: byte; //Code for instruction nBytes: byte; //Num. of bytes of the instruction. Cycles: byte; //Num. of cycles the instruction takes. optCycles: string; //Extra options in Num. of cycles. end; //Record for a 6502 instruction { TP6502Instruct } 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; optCycles: string); end; //Indica el destino de la instrucción TPIC16destin = ( toW = %00000000, //al acumulador toF = %10000000 //a memoria ); const //Constants of address and bit positions for some registers _STATUS = $03; _C = 0; _Z = 2; _RP0 = 5; _RP1 = 6; // _IRP = 7; type {Objeto que representa al hardware de un PIC de la serie 16} { TP6502 } TP6502 = class(TPicCore) public //Campos para desensamblar instrucciones idIns: TP6502Inst; //ID de Instrucción. modIns: TP6502AddMode; //Modo de direccionamiento parIns: word; //Parámetro de instrucción. Válido solo en algunas instrucciones. b_ : byte; //Bit destino. Válido solo en algunas instrucciones. k_ : word; //Parámetro Literal. Válido solo en algunas instrucciones. private //Campos para procesar instrucciones function GetINTCON: byte; function GetINTCON_GIE: boolean; function GetSTATUS: byte; function GetSTATUS_C: boolean; function GetSTATUS_DC: boolean; function GetSTATUS_IRP: boolean; function GetSTATUS_Z: boolean; procedure SetINTCON_GIE(AValue: boolean); procedure SetSTATUS_C(AValue: boolean); procedure SetSTATUS_DC(AValue: boolean); procedure SetSTATUS_IRP(AValue: boolean); procedure SetSTATUS_Z(AValue: boolean); procedure SetFRAM(value: byte); function GetFRAM: byte; public //Campos que modelan a los registros internos W : byte; //Registro de trabajo PC : TWordRec; //PC as record to fast access for bytes PCLATH : byte; //Contador de Programa H STKPTR : 0..7; //Puntero de pila STACK : array[0..7] of word; property STATUS: byte read GetSTATUS; property STATUS_Z: boolean read GetSTATUS_Z write SetSTATUS_Z; property STATUS_C: boolean read GetSTATUS_C write SetSTATUS_C; property STATUS_DC: boolean read GetSTATUS_DC write SetSTATUS_DC; property STATUS_IRP: boolean read GetSTATUS_IRP write SetSTATUS_IRP; 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 procedure Reset; override; function ReadPC: dword; override; procedure WritePC(AValue: dword); override; public //Memories procedure Decode(const opCode: word); //decodifica instrucción function Disassembler(const opCode, par1, par2: byte; out nBytesProc: byte; useVarName: boolean=false): string; //Desensambla la instrucción actual function DisassemblerAt(addr: word; out nBytesProc: byte; useVarName: boolean ): string; override; public //Funciones para la memoria RAM function GetFreeByte(out addr: word; shared: boolean): boolean; function GetFreeBytes(const size: integer; var addr: word): boolean; //obtiene una dirección libre function TotalMemRAM: word; //devuelve el total de memoria RAM function UsedMemRAM: word; //devuelve el total de memoria RAM usada procedure ExploreUsed(rutExplorRAM: TPICRutExplorRAM); //devuelve un reporte del uso de la RAM function ValidRAMaddr(addr: word): boolean; //indica si una posición de memoria es válida function BankToAbsRAM(const offset, bank: byte): word; //devuelve dirección absoluta procedure AbsToBankRAM(const AbsAddr: word; var offset, bank: byte); //convierte dirección absoluta public //Métodos para codificar instrucciones de acuerdo a la sintaxis procedure useRAM; procedure codAsmFD(const inst: TP6502Inst; addMode: TP6502AddMode; param: word); function codInsert(iflash0, nInsert, nWords: integer): boolean; public //Métodos adicionales function FindOpcode(Op: string): TP6502Inst; //busca Opcode procedure GenHex(hexFile: string; ConfigWord: integer = - 1); //genera un archivo hex procedure DumpCode(lOut: TStrings; incAdrr, incCom, incVarNam: boolean); //vuelva en código que contiene public //Initialization constructor Create; override; destructor Destroy; override; end; var //variables globales //mnemónico de las instrucciones PIC16InstName: array[low(TP6502Inst)..high(TP6502Inst)] of TP6502Instruct; implementation { 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; optCycles: string); {Add a new Address Mode including additional information. "optCycles" is a flag and indicate aditional considerations on cycles: '*' -> Add 1 to cycles if page boundery is crossed '**' -> Add 1 to cycles if branch occurs on same page Add 2 to cycles if branch occurs to different page } 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; { TP6502 } procedure TP6502.useRAM; {Marca la posición actual, como usada, e incrementa el puntero iFlash. S ihay error, actualiza el campo "MsjError"} begin //Protección de desborde if iRam >= MaxFlash then begin MsjError := 'FLASH Memory limit exceeded.'; exit; end; ram[iRam].used := true; //marca como usado inc(iRam); end; procedure TP6502.codAsmFD(const inst: TP6502Inst; addMode: TP6502AddMode; param: word); {Codifica las instrucciones orientadas a registro, con sintAxis: NEMÓNICO f,d} var rInst: TP6502Instruct; begin rInst := PIC16InstName[inst]; //Write OpCode ram[iRam].value := rInst.instrInform[addMode].Opcode; useRAM; //marca como usado e incrementa puntero. //Codifica parámetros case addMode of aImplied_, aAcumulat:; //No tiene parámetros aImmediat: begin ram[iRam].value := lo(param); //escribe parámetro useRAM; end; aAbsolute:begin ram[iRam].value := lo(param); useRAM; ram[iRam].value := hi(param); useRAM; end; aZeroPage:begin ram[iRam].value := lo(param); useRAM; end; aRelative:begin ram[iRam].value := lo(param); useRAM; end; aIndirect:begin ram[iRam].value := lo(param); useRAM; ram[iRam].value := hi(param); useRAM; end; aAbsolutX:begin ram[iRam].value := lo(param); useRAM; ram[iRam].value := hi(param); useRAM; end; aAbsolutY:begin ram[iRam].value := lo(param); useRAM; ram[iRam].value := hi(param); useRAM; end; aZeroPagX:begin ram[iRam].value := lo(param); useRAM; end; aZeroPagY:begin ram[iRam].value := lo(param); useRAM; end; aIdxIndir:begin ram[iRam].value := lo(param); useRAM; end; aIndirIdx:begin ram[iRam].value := lo(param); useRAM; end; else raise Exception.Create('Implementation Error.'); end; end; function TP6502.codInsert(iflash0, nInsert, nWords: integer): boolean; {Inserta en la posición iflash0, "nInsert" palabras, desplazando "nWords" palabras. Al final debe quedar "nInsert" palabras de espacio libre en iflash0. Si hay error devuelve FALSE.} var i: Integer; begin Result := True; //By default if iRam+nInsert+nWords-1> MaxFlash 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; //procedure TP6502.BTFSC_sw_BTFSS(iflash0: integer); //{Exchange instruction i_BTFSC to i_BTFSS, or viceversa, in the specified address.} //begin // //Solo necesita cambiar el bit apropiado // flash[iFlash0].value := flash[iFlash0].value XOR %10000000000; //end; function TP6502.FindOpcode(Op: string): TP6502Inst; {Busca una cádena que represente a una instrucción (Opcode). Si encuentra devuelve el identificador de instrucción . Si no encuentra devuelve "i_Inval". } var idInst: TP6502Inst; tmp: String; begin tmp := UpperCase(Op); for idInst := low(TP6502Inst) to high(TP6502Inst) do begin if PIC16InstName[idInst].name = tmp then begin Result := idInst; exit; end; end; //No encontró Result := i_Inval; end; //Campos para procesar instrucciones function TP6502.GetSTATUS: byte; begin Result := ram[_STATUS].dvalue; end; function TP6502.GetSTATUS_Z: boolean; begin Result := (ram[_STATUS].dvalue and %00000100) <> 0; end; procedure TP6502.SetSTATUS_Z(AValue: boolean); begin if AVAlue then ram[_STATUS].dvalue := ram[_STATUS].dvalue or %00000100 else ram[_STATUS].dvalue := ram[_STATUS].dvalue and %11111011; end; function TP6502.GetSTATUS_C: boolean; begin Result := (ram[_STATUS].dvalue and %00000001) <> 0; end; procedure TP6502.SetSTATUS_C(AValue: boolean); begin if AVAlue then ram[_STATUS].dvalue := ram[_STATUS].dvalue or %00000001 else ram[_STATUS].dvalue := ram[_STATUS].dvalue and %11111110; end; function TP6502.GetSTATUS_DC: boolean; begin Result := (ram[_STATUS].dvalue and %00000010) <> 0; end; procedure TP6502.SetSTATUS_DC(AValue: boolean); begin if AVAlue then ram[_STATUS].dvalue := ram[_STATUS].dvalue or %00000010 else ram[_STATUS].dvalue := ram[_STATUS].dvalue and %11111101; end; function TP6502.GetSTATUS_IRP: boolean; begin Result := (ram[_STATUS].dvalue and %10000000) <> 0; end; procedure TP6502.SetSTATUS_IRP(AValue: boolean); begin if AVAlue then ram[_STATUS].dvalue := ram[_STATUS].dvalue or %10000000 else ram[_STATUS].dvalue := ram[_STATUS].dvalue and %01111111; end; 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 if parIns = 0 then begin //Caso especial de direccionamiento indirecto if STATUS_IRP then begin ram[ram[04].value + $100].value := value; end else begin ram[ram[04].value].value := value; end; exit; end; {Se escribe aplicando la máscara de bits implementados. Se podría usra la máscara en lectura o escritura, pero se prefiere hacerlo en escritura, porque se espera que se hagan menos operaciones de escritura que lectura.} case STATUS and %01100000 of %00000000: ram[parIns ].value := value; %00100000: ram[parIns+PICBANKSIZE ].value := value; %01000000: ram[parIns+PICBANKSIZE*2].value := value; %01100000: ram[parIns+PICBANKSIZE*3].value := value; end; 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 if parIns = 0 then begin //Caso especial de direccionamiento indirecto if STATUS_IRP then begin Result := ram[ram[04].value + $100].value; end else begin Result := ram[ram[04].value].value; end; exit; end; case STATUS and %01100000 of %00000000: Result := ram[parIns ].value; %00100000: Result := ram[parIns+ PICBANKSIZE ].value; %01000000: Result := ram[parIns+ PICBANKSIZE*2].value; %01100000: Result := ram[parIns+ PICBANKSIZE*3].value; end; end; procedure TP6502.Decode(const opCode: word); {Decode the value of "opCode" and update: * "idIns" -> Instruction ID * "modIns" -> Address Mode } var i : TP6502Inst; j : TP6502AddMode; inst: TP6502Instruct; iInfom: TInstructInform; begin //Search the Opcode for i := low(TP6502Inst) to high(TP6502Inst) do begin inst := PIC16InstName[i]; for j := low(TP6502AddMode) to high(TP6502AddMode) do begin iInfom := inst.instrInform[j]; if iInfom.Opcode = opCode then begin idIns := i; modIns := j; end; end; end; end; function TP6502.Disassembler(const opCode, par1, par2: byte; out nBytesProc: byte; useVarName: boolean = false): string; {Disassemble the instruction indicated in "Opcode". If the instruction is multibyte it will be used "par1" and "par2" according to the intruction length, which is returned in "nBytesProc". 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; f: word; begin Decode(opCode); //Decode instruction. Update: "idIns", "modIns". nemo := trim(PIC16InstName[idIns].name) + ' '; case modIns of aImplied_: begin nBytesProc := 1; //No parameters needed end; aAcumulat: begin nBytesProc := 1; //No parameters needed end; aImmediat: begin Result := nemo + '#$'+IntToHex(par1,2); nBytesProc := 2; end; aAbsolute: begin Result := nemo + '$'+IntToHex(par1 + par2*256, 4); nBytesProc := 3; end; aZeroPage: begin Result := nemo + '$'+IntToHex(par1, 2); nBytesProc := 2; end; aRelative: begin Result := nemo + '$'+IntToHex(par1, 2); nBytesProc := 2; end; aIndirect: begin Result := nemo + '$('+IntToHex(par1 + par2*256, 4)+')'; nBytesProc := 3; end; aAbsolutX: begin Result := nemo + '$'+IntToHex(par1 + par2*256, 4)+',X'; nBytesProc := 3; end; aAbsolutY: begin Result := nemo + '$'+IntToHex(par1 + par2*256, 4)+',Y'; nBytesProc := 3; end; aZeroPagX: begin Result := nemo + '$'+IntToHex(par1, 2)+',X'; nBytesProc := 3; end; aZeroPagY: begin Result := nemo + '$'+IntToHex(par1, 2)+',Y'; nBytesProc := 3; end; aIdxIndir: begin Result := nemo + '$('+IntToHex(par1, 2)+',X)'; nBytesProc := 2; end; aIndirIdx: begin Result := nemo + '$('+IntToHex(par1, 2)+'),Y'; nBytesProc := 3; end; end; end; function TP6502.DisassemblerAt(addr: word; out nBytesProc: byte; useVarName: boolean): string; {Disassembler the instruction located at "addr"} var valOp: Word; begin Result := Disassembler(ram[addr].value, ram[addr+1].value, ram[addr+2].value, nBytesProc, useVarName); end; function TP6502.CurInstruction: TP6502Inst; {Resturn the instruction pointed by PC, in this moment.} 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; msk, resNib: byte; nCycles, nBytes: byte; resInt : integer; begin //Decodifica instrucción opc := ram[aPC].value; Decode(opc); //Decode instruction nCycles := PIC16InstName[idIns].instrInform[modIns].Cycles; nBytes := PIC16InstName[idIns].instrInform[modIns].nBytes; case idIns of 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 i_Inval: begin MsjError := 'Invalid Opcode'; end; end; { i_ADDWF: begin resByte := FRAM; resWord := W + resByte; resNib := (W and $0F) + (resByte and $0F); if modIns = toF then begin FRAM := resWord and $FF; end else begin //toW w := resWord and $FF; end; STATUS_Z := (resWord and $ff) = 0; STATUS_C := (resWord > 255); STATUS_DC := (resNib > 15); end; i_ANDWF: begin resByte := W and FRAM; if modIns = toF then begin FRAM := resByte; end else begin //toW w := resByte; end; STATUS_Z := resByte = 0; end; i_CLRF: begin FRAM := 0; STATUS_Z := true; end; i_CLRW: begin W := 0; STATUS_Z := true; end; i_COMF : begin resByte := not FRAM; if modIns = toF then begin FRAM := resByte; end else begin //toW w := resByte; end; STATUS_Z := resByte = 0; end; i_DECF : begin resByte := FRAM; if resByte = 0 then resByte := $FF else dec(resByte); if modIns = toF then begin FRAM := resByte; end else begin //toW w := resByte; end; STATUS_Z := resByte = 0; end; i_DECFSZ: begin resByte := FRAM; if resByte = 0 then resByte := $FF else dec(resByte); if modIns = toF then begin FRAM := resByte; end else begin //toW w := resByte; end; STATUS_Z := resByte = 0; if STATUS_Z then begin Inc(PC.W); //Jump one instrucción Inc(nClck); //In this case it takes one more cicle end; end; i_INCF: begin resByte := FRAM; if resByte = 255 then resByte := 0 else inc(resByte); if modIns = toF then begin FRAM := resByte; end else begin //toW w := resByte; end; STATUS_Z := resByte = 0; end; i_INCFSZ: begin resByte := FRAM; if resByte = 255 then resByte := 0 else inc(resByte); if modIns = toF then begin FRAM := resByte; end else begin //toW w := resByte; end; STATUS_Z := resByte = 0; if STATUS_Z then begin Inc(PC.W); //Jump one instrucción Inc(nClck); //In this case it takes one more cicle end; end; i_IORWF: begin resByte := W or FRAM; if modIns = toF then begin FRAM := resByte; end else begin //toW w := resByte; end; STATUS_Z := resByte <> 0; end; i_MOVF: begin resByte := FRAM; if modIns = toF then begin //no mueve, solo verifica STATUS_Z := (resByte = 0); end else begin //toW w := resByte; STATUS_Z := (resByte = 0); end; end; i_MOVWF: begin FRAM := W; //escribe a donde esté mapeado, (si está mapeado) if parIns = $02 then begin //Es el PCL PC.H := PCLATH; //Cuando se escribe en PCL, se carga PCH con PCLATH end; end; i_NOP: begin end; i_RLF: begin resByte := FRAM; bit7 := resByte and $80; //guarda bit 7 resByte := (resByte << 1) and $ff; //desplaza //pone C en bit bajo if STATUS_C then begin //C era 1 resByte := resByte or $01; //pone a 1 el bit 0 end else begin //C era 0 //no es necesario agregarlo, porque por defecto se agrega 0 end; //Actualiza C if bit7 = 0 then STATUS_C := false else STATUS_C := true; if modIns = toF then begin FRAM := resByte; end else begin //toW w := resByte; end; end; i_RRF: begin resByte := FRAM; bit0 := resByte and $01; //guarda bit 0 resByte := resByte >> 1; //desplaza //pone C en bit alto if STATUS_C then begin //C era 1 resByte := resByte or $80; //pone a 1 el bit 0 end else begin //C era 0 //no es necesario agregarlo, porque por defecto se agrega 0 end; //Actualiza C if bit0 = 0 then STATUS_C := false else STATUS_C := true; if modIns = toF then begin FRAM := resByte; end else begin //toW w := resByte; end; end; i_SUBWF: begin resByte := FRAM; resInt := resByte - W; if modIns = toF then begin FRAM := resInt and $FF; end else begin //toW w := resInt and $FF; end; STATUS_Z := (resInt = 0); if resInt < 0 then STATUS_C := false //negativo else STATUS_C := true; resInt := (resByte and $0F) - (W and $0F); if resInt < 0 then STATUS_DC := false //negativo else STATUS_DC := true; end; i_SWAPF: begin resByte := FRAM; FRAM := (resByte >> 4) or (resByte << 4); end; i_XORWF: begin resByte := W xor FRAM; if modIns = toF then begin FRAM := resByte; end else begin //toW w := resByte; end; STATUS_Z := resByte <> 0; end; //BIT-ORIENTED FILE REGISTER OPERATIONS i_BCF: begin msk := $1 << b_; msk := not msk; FRAM := FRAM and msk; end; i_BSF: begin msk := $1 << b_; FRAM := FRAM or msk;// b_ end; i_BTFSC: begin msk := $1 << b_; if (FRAM and msk) = 0 then begin Inc(PC.W); //Jump one instrucción Inc(nClck); //In this case it takes one more cicle end; end; i_BTFSS: begin msk := $1 << b_; if (FRAM and msk) <> 0 then begin Inc(PC.W); //Jump one instrucción Inc(nClck); //In this case it takes one more cicle end; end; //LITERAL AND CONTROL OPERATIONS i_ADDLW: begin resWord := W + k_; resNib := (W and $0F) + (k_ and $0F); w := resWord and $FF; STATUS_Z := (resWord and $ff) = 0; STATUS_C := (resWord > 255); STATUS_DC := (resNib > 15); end; i_ANDLW: begin resByte := W and K_; w := resByte; STATUS_Z := resByte = 0; end; i_CALL: begin //Guarda dirección en Pila STACK[STKPTR] := PC.W; if STKPTR = 7 then begin //Desborde de pila STKPTR := 0; if OnExecutionMsg<>nil then OnExecutionMsg('Stack Overflow on CALL OpCode at $' + IntToHex(aPC,4)); end else begin STKPTR := STKPTR +1; end; PC.W := k_; //Takes the 11 bits from k PC.H := PC.H or (PCLATH and %00011000); //And complete with bits 3 and 4 of PCLATH Inc(nClck,2); //This instruction takes two cicles exit; end; i_CLRWDT: begin end; i_GOTO: begin PC.W := k_; //Takes the 11 bits from k PC.H := PC.H or (PCLATH and %00011000); //And complete with bits 3 and 4 of PCLATH Inc(nClck,2); //This instruction takes two cicles exit; end; i_IORLW: begin resByte := W or k_; w := resByte; STATUS_Z := resByte <> 0; end; i_MOVLW: begin W := k_; end; i_RETFIE: begin //Saca dirección en Pila if STKPTR = 0 then begin //Desborde de pila STKPTR := 7; if OnExecutionMsg<>nil then OnExecutionMsg('Stack Overflow on RETFIE OpCode at $' + IntToHex(aPC,4)); end else begin STKPTR := STKPTR - 1; end; PC.W := STACK[STKPTR]; //Should be 13 bits Inc(nClck); //Esta instrucción toma un ciclo más //Activa GIE INTCON_GIE := true; end; i_RETLW: begin //Saca dirección en Pila if STKPTR = 0 then begin //Desborde de pila STKPTR := 7; if OnExecutionMsg<>nil then OnExecutionMsg('Stack Overflow on RETLW OpCode at $' + IntToHex(aPC,4)); end else begin STKPTR := STKPTR - 1; end; PC.W := STACK[STKPTR]; //Should be 13 bits Inc(nClck); //Esta instrucción toma un ciclo más //Fija valor en W W := k_; end; i_RETURN: begin //Saca dirección en Pila if STKPTR = 0 then begin //Desborde de pila STKPTR := 7; if OnExecutionMsg<>nil then OnExecutionMsg('Stack Overflow on RETURN OpCode at $' + IntToHex(aPC,4)); end else begin STKPTR := STKPTR - 1; end; PC.W := STACK[STKPTR]; //Should be 13 bits Inc(nClck); //Esta instrucción toma un ciclo más end; i_SLEEP: begin end; i_SUBLW: begin resInt := k_ - W; w := resInt and $FF; STATUS_Z := (resInt = 0); if resInt < 0 then STATUS_C := false //negativo else STATUS_C := true; resInt := (k_ and $0F) - (W and $0F); if resInt < 0 then STATUS_DC := false //negativo else STATUS_DC := true; end; i_XORLW: begin resByte := W xor k_; w := resByte; STATUS_Z := resByte <> 0; end; i_Inval: begin MsjError := 'Invalid Opcode'; end; 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 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; //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; if not ram[_pc].used then begin //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; procedure TP6502.Reset; //Reinicia el dipsoitivo var i: Integer; begin PC.W := 0; PCLATH := 0; W := 0; STKPTR := 0; //Posición inicial del puntero de pila nClck := 0; //Inicia contador de ciclos CommStop := false; //Limpia bandera //Limpia solamente el valor inicial, no toca los otros campos for i:=0 to high(ram) do begin ram[i].dvalue := $00; end; ram[_STATUS].dvalue := %00011000; //STATUS 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 function TP6502.GetFreeByte(out addr: word; shared: boolean): boolean; {Devuelve una dirección libre de la memoria flash. "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; maxRam: word; begin Result := false; //valor inicial maxRam := NumBanks * PICBANKSIZE; //posición máxima //Realmente debería explorar solo hasta la dirección implementada, por eficiencia for i:=0 to maxRam-1 do begin if (ram[i].state = cs_impleGPR) and (not ram[i].used) then begin //Esta dirección está libre ram[i].used := true; //marca como usado if shared then begin ram[i].shared := true; //Marca como compartido end; addr := i; //Notar que la posición de memoria puede estar mapeada a otro banco. Result := true; //indica que encontró espacio exit; end; end; end; function TP6502.GetFreeBytes(const size: integer; var addr: word): boolean; {Devuelve una dirección libre de la memoria flash (y el banco) para ubicar un bloque del tamaño indicado. Si encuentra espacio, devuelve TRUE. El tamaño se da en bytes, pero si el valor es negativo, se entiende que es en bits.} var i: word; maxRam: Word; begin Result := false; //valor por defecto if size=0 then exit; maxRam := word(NumBanks * PICBANKSIZE) - 1; for i:=0 to maxRam do begin //verifica 1 a 1, por seguridad if HaveConsecRAM(i, size, maxRam) then begin //encontró del tamaño buscado UseConsecRAM(i, size); //marca como usado addr := i; Result := true; //indica que encontró espacio exit; end; end; end; function TP6502.TotalMemRAM: word; {Devuelve el total de memoria RAM disponible} var i: Integer; begin Result := 0; for i := 0 to word(NumBanks * PICBANKSIZE) - 1 do begin if ram[i].AvailGPR then begin 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 word(NumBanks * PICBANKSIZE) - 1 do begin if ram[i].AvailGPR and (ram[i].used) then begin //Notar que "AvailGPR" asegura que no se consideran registros maepados Result := Result + 1; end; end; end; procedure TP6502.ExploreUsed(rutExplorRAM: TPICRutExplorRAM); {Genera un reporte de uso de RAM} var i: Integer; begin for i := 0 to word(NumBanks * PICBANKSIZE) - 1 do begin if ram[i].AvailGPR and (ram[i].used) then begin rutExplorRAM(i, 0, @ram[i]); end; end; end; function TP6502.ValidRAMaddr(addr: word): boolean; {Indica si la dirección indicada es válida dentro del hardware del PIC} begin if addr > PICBANKSIZE*NumBanks then exit(false); //excede límite exit(true); end; function TP6502.BankToAbsRAM(const offset, bank: byte): word; {Convierte una dirección y banco a una dirección absoluta} begin Result := bank * PICBANKSIZE + offset; end; procedure TP6502.AbsToBankRAM(const AbsAddr: word; var offset, bank: byte); {Convierte dirección absoluta a dirección en bancos} begin offset := AbsAddr and %01111111; bank := AbsAddr >> 7; end; procedure TP6502.GenHex(hexFile: string; ConfigWord: integer = -1); {Genera el archivo *.hex, a partir de los datos almacenados en la memoria FLASH. Actualiza los campos, minUsed y maxUsed.} var cfg, tmp: String; iHex: word; //Índice para explorar dat: String; //Cadena de dígitos hexadecimales addr: word; //Dirección de inicio const MAX_INS_HEX = 8; //Número máximo de instrucciones que devuelve por pasada function ExtractHex(out Addre: word): string; {Devuelve una cadena (de longitud que varía desde 0, hasta MAX_INS_HEX*4 caracteres) con valores en hexadecimal de isntrucciones, consecutivas usadas, en le memoria FLASH. La lectura se hace a partir de iHex, y los caracteres en hexadecimal se escriben en 4 dígitos, en la misma forma que se usan para los archivos *.HEX. En "Addr" devuelve la dirección absoluta de inicio desde donde lee. Con cada llamada, devuelve los bloques consecutivos de datos. Si no hay más datos devuelve cadena vacía.} var p1, p2: word; cont, p: word; tmp: String; begin Result := ''; //Busca inicio de instrucciones usadas, desde la posición iHex while (iHex=PICMAXFLASH then begin //Llegó al final exit; //sale con cadena nula end; //Ya encontró el inicio ahora busca celdas consecutivas p1 := iHex; Addre := p1; cont := 2; //inicia contador inc(iHex); //pasa al siguiente while (iHex=PICMAXFLASH then begin //Salió porque Llegó al final p2 := PICMAXFLASH-1; end else if cont>=MAX_INS_HEX then begin //Salió porque llegó al máximo de celdas if ram[iHex].used then begin //La ultima celda estaba ocupada p2 := iHex; inc(iHex); //deja listo para la siguiente exploración end else begin //La ultima celda estaba ocupada p2 := iHex-1; //iHex, queda apuntando a la siguiente celda end; end else begin //Salió porque encontró celda sin usar p2 := iHex-1; //iHex, queda apuntando a la siguiente celda end; //Ya tiene las dos posiciones tmp := ''; for p:=p1 to p2 do begin if p1maxUsed then maxUsed := p2; //Actualiza tmp := IntToHex(ram[p].value, 4); Result +=copy(tmp,3,2) + copy(tmp,1,2); //se graba con los bytes invertidos end; end; begin hexLines.Clear; //Se usará la lista hexLines GenHexExAdd($0000); //Prepara extracción de datos minUsed := PICMAXFLASH; maxUsed := 0; iHex := 0; //Inicia la extracción de código dat := ExtractHex(addr); while dat <>'' do begin GenHexData(addr, dat); dat := ExtractHex(addr); end; //Bits de configuración tmp := ''; if ConfigWord<>-1 then begin //Se pide generar bits de configuración {Los bits de configuración para la serie 16F, se almacenan en: Config: 0x2007 (0x400E in the HEX file) EEPROM: 0x2100 (0x4200 in the HEX file) } cfg := IntToHex(ConfigWord and $FFFF, 4); tmp +=copy(cfg,3,2) + copy(cfg,1,2); //se graba con los bytes invertidos GenHexData($2007, tmp); end; GenHexEOF; //Fin de archivo GenHexComm(self.Model); //Comentario hexLines.SaveToFile(hexFile); //Genera archivo end; procedure TP6502.DumpCode(lOut: TStrings; incAdrr, incCom, incVarNam: boolean); {Desensambla las instrucciones grabadas en el PIC. Se debe llamar despues de llamar a GenHex(), para que se actualicen las variables} var i: Word; lblLin, comLat, comLin, lin: String; nBytes: byte; begin //Se supone que minUsed y maxUsed, ya deben haber sido actualizados. i := minUsed; while i <= maxUsed do begin //Lee comentarios y etiqueta lblLin := ram[i].topLabel; comLat := ram[i].sideComment; comLin := ram[i].topComment; //Escribe etiqueta al inicio de línea if lblLin<>'' then lOut.Add(lblLin+':'); //Escribe comentario al inicio de línea if incCom and (comLin<>'') then begin lOut.Add(comLin); end; //Decodifica instrucción lin := DisassemblerAt(i, nBytes, incVarNam); //Instrucción i := i + nBytes; //Verificas si incluye dirección física if incAdrr then begin lin := '0x'+IntToHex(i,3) + ' ' + lin; end; //Verifica si incluye comentario lateral if incCom then begin lin := lin + ' ' + comLat; end; lOut.Add(' ' + lin); end; end; constructor TP6502.Create; begin inherited Create; PICBANKSIZE := 128; //RAM bank size PICMAXRAM := PICBANKSIZE * 4; //Máx RAM memory (4 banks) PICPAGESIZE := 2048; PICMAXFLASH := PICPAGESIZE * 4; //Máx Flash memeory (4 pages) SetLength(ram, PICMAXRAM); //Default hardware settings NumBanks:=2; //Número de bancos de RAM. Por defecto se asume 2 NumPages:=1; //Número de páginas de memoria Flash. Por defecto 1 MaxFlash := PICPAGESIZE; //En algunos casos, puede ser menor al tamaño de una página //inicia una configuración común ClearMemRAM; SetStatRAM($020, $04F, cs_impleGPR); //estado inicial iRam := 0; //posición de inicio ClearMemFlash; 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 /////////////////////////////////////////////////////////////////// ////////////////////////////// ADC //////////////////////////////// PIC16InstName[i_ADC].name := 'ADC'; //Add Memory to Accumulator with Carry PIC16InstName[i_ADC].AddAddressMode(aImmediat,$69,2,2,''); PIC16InstName[i_ADC].AddAddressMode(aZeroPage,$65,2,3,''); PIC16InstName[i_ADC].AddAddressMode(aZeroPagX,$75,2,4,''); PIC16InstName[i_ADC].AddAddressMode(aAbsolute,$6D,3,4,''); PIC16InstName[i_ADC].AddAddressMode(aAbsolutX,$7D,3,4,'*'); PIC16InstName[i_ADC].AddAddressMode(aAbsolutY,$79,3,4,'*'); PIC16InstName[i_ADC].AddAddressMode(aIdxIndir,$61,2,6,''); PIC16InstName[i_ADC].AddAddressMode(aIndirIdx,$71,2,5,'*'); PIC16InstName[i_AND].name := 'AND'; //AND Memory with Accumulator PIC16InstName[i_AND].AddAddressMode(aImmediat,$29,2,2,''); PIC16InstName[i_AND].AddAddressMode(aZeroPage,$25,2,3,''); PIC16InstName[i_AND].AddAddressMode(aZeroPagX,$35,2,4,''); PIC16InstName[i_AND].AddAddressMode(aAbsolute,$2D,3,4,''); PIC16InstName[i_AND].AddAddressMode(aAbsolutX,$3D,3,4,'*'); PIC16InstName[i_AND].AddAddressMode(aAbsolutY,$39,3,4,'*'); PIC16InstName[i_AND].AddAddressMode(aIdxIndir,$21,2,6,''); PIC16InstName[i_AND].AddAddressMode(aIndirIdx,$31,2,5,'*'); PIC16InstName[i_ASL].name := 'ASL'; //Shift Left One Bit (MemoryorAccumulator) PIC16InstName[i_ASL].AddAddressMode(aAcumulat,$0A,1,2,''); PIC16InstName[i_ASL].AddAddressMode(aZeroPage,$06,2,5,''); PIC16InstName[i_ASL].AddAddressMode(aZeroPagX,$16,2,6,''); PIC16InstName[i_ASL].AddAddressMode(aAbsolute,$0E,3,6,''); PIC16InstName[i_ASL].AddAddressMode(aAbsolutX,$1E,3,7,''); PIC16InstName[i_BCC].name := 'BCC'; //Branch on Carry Clear PIC16InstName[i_BCC].AddAddressMode(aRelative,$90,2,2,'**'); PIC16InstName[i_BCS].name := 'BCS'; //Branch on Carry Set PIC16InstName[i_BCS].AddAddressMode(aRelative,$B0,2,2,'**'); PIC16InstName[i_BEQ].name := 'BEQ'; //Branch on Result Zero PIC16InstName[i_BEQ].AddAddressMode(aRelative,$F0,2,2,'**'); PIC16InstName[i_BIT].name := 'BIT'; //Test Bits in Memory with Accumulator PIC16InstName[i_BIT].AddAddressMode(aZeroPage,$24,2,3,''); PIC16InstName[i_BIT].AddAddressMode(aAbsolute,$2C,3,4,''); PIC16InstName[i_BMI].name := 'BMI'; //Branch on Result Minus PIC16InstName[i_BMI].AddAddressMode(aRelative,$30,2,2,'**'); PIC16InstName[i_BNE].name := 'BNE'; //Branch on Result not Zero PIC16InstName[i_BNE].AddAddressMode(aRelative,$D0,2,2,'**'); PIC16InstName[i_BPL].name := 'BPL'; //Branch on Result Plus PIC16InstName[i_BPL].AddAddressMode(aRelative,$10,2,2,'**'); PIC16InstName[i_BRK].name := 'BRK'; //Force Break PIC16InstName[i_BRK].AddAddressMode(aImplied_,$00,1,7,''); PIC16InstName[i_BVC].name := 'BVC'; //Branch on Overflow Clear PIC16InstName[i_BVC].AddAddressMode(aRelative,$50,2,2,'**'); PIC16InstName[i_BVS].name := 'BVS'; //Branch on Overflow Set PIC16InstName[i_BVS].AddAddressMode(aRelative,$70,2,2,'**'); PIC16InstName[i_CLC].name := 'CLC'; //Clear Carry Flag PIC16InstName[i_CLC].AddAddressMode(aImplied_,$18,1,2,''); PIC16InstName[i_CLD].name := 'CLD'; //Clear Decimal Mode PIC16InstName[i_CLD].AddAddressMode(aImplied_,$D8,1,2,''); PIC16InstName[i_CLI].name := 'CLI'; //Clear Interrupt Disable Bit PIC16InstName[i_CLI].AddAddressMode(aImplied_,$58,1,2,''); PIC16InstName[i_CLV].name := 'CLV'; //Clear Overflow Flag PIC16InstName[i_CLV].AddAddressMode(aImplied_,$B8,1,2,''); PIC16InstName[i_CMP].name := 'CMP'; //Compare Memory with Accumulator PIC16InstName[i_CMP].AddAddressMode(aImmediat,$C9,2,2,''); PIC16InstName[i_CMP].AddAddressMode(aZeroPage,$C5,2,3,''); PIC16InstName[i_CMP].AddAddressMode(aZeroPagX,$D5,2,4,''); PIC16InstName[i_CMP].AddAddressMode(aAbsolute,$CD,3,4,''); PIC16InstName[i_CMP].AddAddressMode(aAbsolutX,$DD,3,4,'*'); PIC16InstName[i_CMP].AddAddressMode(aAbsolutY,$D9,3,4,'*'); PIC16InstName[i_CMP].AddAddressMode(aIdxIndir,$C1,2,6,''); PIC16InstName[i_CMP].AddAddressMode(aIndirIdx,$D1,2,5,'*'); PIC16InstName[i_CPX].name := 'CPX'; //Compare Memory and Index X PIC16InstName[i_CPX].AddAddressMode(aImmediat,$E0,2,2,''); PIC16InstName[i_CPX].AddAddressMode(aZeroPage,$E4,2,3,''); PIC16InstName[i_CPX].AddAddressMode(aAbsolute,$EC,3,4,''); PIC16InstName[i_CPY].name := 'CPY'; //Compare Memory and Index Y PIC16InstName[i_CPY].AddAddressMode(aImmediat,$C0,2,2,''); PIC16InstName[i_CPY].AddAddressMode(aZeroPage,$C4,2,3,''); PIC16InstName[i_CPY].AddAddressMode(aAbsolute,$CC,3,4,''); PIC16InstName[i_DEC].name := 'DEC'; //Decrement Memory by One PIC16InstName[i_DEC].AddAddressMode(aZeroPage,$C6,2,5,''); PIC16InstName[i_DEC].AddAddressMode(aZeroPagX,$D6,2,6,''); PIC16InstName[i_DEC].AddAddressMode(aAbsolute,$CE,3,3,''); PIC16InstName[i_DEC].AddAddressMode(aAbsolutX,$DE,3,7,''); PIC16InstName[i_DEX].name := 'DEX'; //Decrement Index X by One PIC16InstName[i_DEX].AddAddressMode(aImplied_,$CA,1,2,''); PIC16InstName[i_DEY].name := 'DEY'; //Decrement Index Y by One PIC16InstName[i_DEY].AddAddressMode(aImplied_,$88,1,2,''); PIC16InstName[i_EOR].name := 'EOR'; //Exclusive-OR Memory with Accumulator PIC16InstName[i_EOR].AddAddressMode(aImmediat,$49,2,2,''); PIC16InstName[i_EOR].AddAddressMode(aZeroPage,$45,2,3,''); PIC16InstName[i_EOR].AddAddressMode(aZeroPagX,$55,2,4,''); PIC16InstName[i_EOR].AddAddressMode(aAbsolute,$4D,3,4,''); PIC16InstName[i_EOR].AddAddressMode(aAbsolutX,$5D,3,4,'*'); PIC16InstName[i_EOR].AddAddressMode(aAbsolutY,$59,3,4,'*'); PIC16InstName[i_EOR].AddAddressMode(aIdxIndir,$41,2,6,''); PIC16InstName[i_EOR].AddAddressMode(aIndirIdx,$51,2,5,'*'); PIC16InstName[i_INC].name := 'INC'; //Increment Memory by One PIC16InstName[i_INC].AddAddressMode(aZeroPage,$E6,2,5,''); PIC16InstName[i_INC].AddAddressMode(aZeroPagX,$F6,2,6,''); PIC16InstName[i_INC].AddAddressMode(aAbsolute,$EE,3,6,''); PIC16InstName[i_INC].AddAddressMode(aAbsolutX,$FE,3,7,''); PIC16InstName[i_INX].name := 'INX'; //Increment Index X by One PIC16InstName[i_INX].AddAddressMode(aImplied_,$E8,1,2,''); PIC16InstName[i_INY].name := 'INY'; //Increment Index Y by One PIC16InstName[i_INY].AddAddressMode(aImplied_,$C8,1,2,''); PIC16InstName[i_JMP].name := 'JMP'; //Jump to New Location PIC16InstName[i_JMP].AddAddressMode(aAbsolute,$4C,3,3,''); PIC16InstName[i_JMP].AddAddressMode(aIndirect,$6C,3,5,''); PIC16InstName[i_JSR].name := 'JSR'; //Jump to New Location Saving Return Address PIC16InstName[i_JSR].AddAddressMode(aAbsolute,$20,3,6,''); PIC16InstName[i_LDA].name := 'LDA'; //Load Accumulator with Memory PIC16InstName[i_LDA].AddAddressMode(aImmediat,$A9,2,2,''); PIC16InstName[i_LDA].AddAddressMode(aZeroPage,$A5,2,3,''); PIC16InstName[i_LDA].AddAddressMode(aZeroPagX,$B5,2,4,''); PIC16InstName[i_LDA].AddAddressMode(aAbsolute,$AD,3,4,''); PIC16InstName[i_LDA].AddAddressMode(aAbsolutX,$BD,3,4,'*'); PIC16InstName[i_LDA].AddAddressMode(aAbsolutY,$B9,3,4,'*'); PIC16InstName[i_LDA].AddAddressMode(aIdxIndir,$A1,2,6,''); PIC16InstName[i_LDA].AddAddressMode(aIndirIdx,$B1,2,5,'*'); PIC16InstName[i_LDX].name := 'LDX'; //Load Index X with Memory PIC16InstName[i_LDX].AddAddressMode(aImmediat,$A2,2,2,''); PIC16InstName[i_LDX].AddAddressMode(aZeroPage,$A6,2,3,''); PIC16InstName[i_LDX].AddAddressMode(aZeroPagY,$B6,2,4,''); PIC16InstName[i_LDX].AddAddressMode(aAbsolute,$AE,3,4,''); PIC16InstName[i_LDX].AddAddressMode(aAbsolutY,$BE,3,4,'*'); PIC16InstName[i_LDY].name := 'LDY'; //Load Index Y with Memory PIC16InstName[i_LDY].AddAddressMode(aImmediat,$A0,2,2,''); PIC16InstName[i_LDY].AddAddressMode(aZeroPage,$A4,2,3,''); PIC16InstName[i_LDY].AddAddressMode(aZeroPagX,$B4,2,4,''); PIC16InstName[i_LDY].AddAddressMode(aAbsolute,$AC,3,4,''); PIC16InstName[i_LDY].AddAddressMode(aAbsolutX,$BC,3,4,'*'); PIC16InstName[i_LSR].name := 'LSR'; //Shift One Bit Right (Memory orAccumulator) PIC16InstName[i_LSR].AddAddressMode(aAcumulat,$4A,1,2,''); PIC16InstName[i_LSR].AddAddressMode(aZeroPage,$46,2,5,''); PIC16InstName[i_LSR].AddAddressMode(aZeroPagX,$56,2,6,''); PIC16InstName[i_LSR].AddAddressMode(aAbsolute,$4E,3,6,''); PIC16InstName[i_LSR].AddAddressMode(aAbsolutX,$5E,3,7,''); PIC16InstName[i_NOP].name := 'NOP'; //No Operation PIC16InstName[i_NOP].AddAddressMode(aImplied_,$EA,1,2,''); PIC16InstName[i_ORA].name := 'ORA'; //OR Memory with Accumulator PIC16InstName[i_ORA].AddAddressMode(aImmediat,$09,2,2,''); PIC16InstName[i_ORA].AddAddressMode(aZeroPage,$05,2,3,''); PIC16InstName[i_ORA].AddAddressMode(aZeroPagX,$15,2,4,''); PIC16InstName[i_ORA].AddAddressMode(aAbsolute,$0D,3,4,''); PIC16InstName[i_ORA].AddAddressMode(aAbsolutX,$1D,3,4,'*'); PIC16InstName[i_ORA].AddAddressMode(aAbsolutY,$19,3,4,'*'); PIC16InstName[i_ORA].AddAddressMode(aIdxIndir,$01,2,6,''); PIC16InstName[i_ORA].AddAddressMode(aIndirIdx,$11,2,5,'*'); PIC16InstName[i_PHA].name := 'PHA'; //Push Accumulator on Stack PIC16InstName[i_PHA].AddAddressMode(aImplied_,$48,1,3,''); PIC16InstName[i_PHP].name := 'PHP'; //Push Processor Status on Stack PIC16InstName[i_PHP].AddAddressMode(aImplied_,$08,1,3,''); PIC16InstName[i_PLA].name := 'PLA'; //Pull Accumulator from Stack PIC16InstName[i_PLA].AddAddressMode(aImplied_,$68,1,4,''); PIC16InstName[i_PLP].name := 'PLP'; //Pull Processor Status fromStack PIC16InstName[i_PLP].AddAddressMode(aImplied_,$28,1,4,''); PIC16InstName[i_ROL].name := 'ROL'; //Rotate One Bit Left (Memory orAccumulator) PIC16InstName[i_ROL].AddAddressMode(aAcumulat,$2A,1,2,''); PIC16InstName[i_ROL].AddAddressMode(aZeroPage,$26,2,5,''); PIC16InstName[i_ROL].AddAddressMode(aZeroPagX,$36,2,6,''); PIC16InstName[i_ROL].AddAddressMode(aAbsolute,$2E,3,6,''); PIC16InstName[i_ROL].AddAddressMode(aAbsolutX,$3E,3,7,''); PIC16InstName[i_ROR].name := 'ROR'; //Rotate One Bit Right (Memory or Accumulator) PIC16InstName[i_ROR].AddAddressMode(aAcumulat,$6A,1,2,''); PIC16InstName[i_ROR].AddAddressMode(aZeroPage,$66,2,5,''); PIC16InstName[i_ROR].AddAddressMode(aZeroPagX,$76,2,6,''); PIC16InstName[i_ROR].AddAddressMode(aAbsolute,$6E,3,6,''); PIC16InstName[i_ROR].AddAddressMode(aAbsolutX,$7E,3,7,''); PIC16InstName[i_RTI].name := 'RTI'; //Return from Interrupt PIC16InstName[i_RTI].AddAddressMode(aImplied_,$40,1,6,''); PIC16InstName[i_RTS].name := 'RTS'; //Return from Subroutine PIC16InstName[i_RTS].AddAddressMode(aImplied_,$60,1,6,''); PIC16InstName[i_SBC].name := 'SBC'; //Subtract Memory from Accumulator withBorrow PIC16InstName[i_SBC].AddAddressMode(aImmediat,$E9,2,2,''); PIC16InstName[i_SBC].AddAddressMode(aZeroPage,$E5,2,3,''); PIC16InstName[i_SBC].AddAddressMode(aZeroPagX,$F5,2,4,''); PIC16InstName[i_SBC].AddAddressMode(aAbsolute,$ED,3,4,''); PIC16InstName[i_SBC].AddAddressMode(aAbsolutX,$FD,3,4,'*'); PIC16InstName[i_SBC].AddAddressMode(aAbsolutY,$F9,3,4,'*'); PIC16InstName[i_SBC].AddAddressMode(aIdxIndir,$E1,2,6,''); PIC16InstName[i_SBC].AddAddressMode(aIndirIdx,$F1,2,5,'*'); PIC16InstName[i_SEC].name := 'SEC'; //Set Carry Flag PIC16InstName[i_SEC].AddAddressMode(aImplied_,$38,1,2,''); PIC16InstName[i_SED].name := 'SED'; //Set Decimal Flag PIC16InstName[i_SED].AddAddressMode(aImplied_,$F8,1,2,''); PIC16InstName[i_SEI].name := 'SEI'; //Set Interrupt Disable Status PIC16InstName[i_SEI].AddAddressMode(aImplied_,$78,1,2,''); PIC16InstName[i_STA].name := 'STA'; //Store Accumulator in Memory PIC16InstName[i_STA].AddAddressMode(aZeroPage,$85,2,3,''); PIC16InstName[i_STA].AddAddressMode(aZeroPagX,$95,2,4,''); PIC16InstName[i_STA].AddAddressMode(aAbsolute,$8D,3,4,''); PIC16InstName[i_STA].AddAddressMode(aAbsolutX,$9D,3,5,''); PIC16InstName[i_STA].AddAddressMode(aAbsolutY,$99,3,5,''); PIC16InstName[i_STA].AddAddressMode(aIdxIndir,$81,2,6,''); PIC16InstName[i_STA].AddAddressMode(aIndirIdx,$91,2,6,''); PIC16InstName[i_STX].name := 'STX'; //Store Index X in Memory PIC16InstName[i_STX].AddAddressMode(aZeroPage,$86,2,3,''); PIC16InstName[i_STX].AddAddressMode(aZeroPagY,$96,2,4,''); PIC16InstName[i_STX].AddAddressMode(aAbsolute,$8E,3,4,''); PIC16InstName[i_STY].name := 'STY'; //Sore Index Y in Memory PIC16InstName[i_STY].AddAddressMode(aZeroPage,$84,2,3,''); PIC16InstName[i_STY].AddAddressMode(aZeroPagX,$94,2,4,''); PIC16InstName[i_STY].AddAddressMode(aAbsolute,$8C,3,4,''); PIC16InstName[i_TAX].name := 'TAX'; //Transfer Accumulator to IndexX PIC16InstName[i_TAX].AddAddressMode(aImplied_,$AA,1,2,''); PIC16InstName[i_TAY].name := 'TAY'; //Transfer Accumulator to IndexY PIC16InstName[i_TAY].AddAddressMode(aImplied_,$A8,1,2,''); PIC16InstName[i_TSX].name := 'TSX'; //Transfer Stack Pointer toIndex X PIC16InstName[i_TSX].AddAddressMode(aImplied_,$BA,1,2,''); PIC16InstName[i_TXA].name := 'TXA'; //Transfer Index X to Accumulator PIC16InstName[i_TXA].AddAddressMode(aImplied_,$8A,1,2,''); PIC16InstName[i_TXS].name := 'TXS'; //Transfer Index X to StackRegister PIC16InstName[i_TXS].AddAddressMode(aImplied_,$9A,1,2,''); PIC16InstName[i_TYA].name := 'TYA'; //Transfer Index Y to Accumulator PIC16InstName[i_TYA].AddAddressMode(aImplied_,$98,1,2,''); PIC16InstName[i_Inval].name := 'Inval'; end; initialization InitTables; end.