{PICCore Contains basic definitions applicable to all PIC microcontroller Cores Created by Tito Hinostroza 28/04/2018 } unit CPUCore; {$mode objfpc}{$H+} interface uses Classes, SysUtils; type //Union to access bytes of a word TWordRec = record case byte of 1 : (W : Word); {$IFDEF ENDIAN_LITTLE} 2 : (L, H: Byte); {$ELSE} 2 : (H, L: Byte); {$ENDIF} end; TCPUCellState = ( cs_impleSFR, //Implemented. Used by Oeprative System o Kernel cs_impleGPR, //Implemented. Can be used. cs_unimplem //Not implemented. ); TCPURamUsed = ( ruUnused, //(NOT included in PRG output file). ruCodeOp, //Used for code Opcode (included in PRG output file). ruCodeDa, //Used for code Operand (included in PRG output file). ruData, //Used for variables (included in PRG output file). ruAbsData //Used for variables in absolute positions (NOT included in PRG output file). ); type //Models for RAM memory { TCPURamCell } {Modela a una dirección lógica de la memoria RAM. Se ha taratdo de hacer una definición eficiente de esta estructura para facilitar la implementación de simuladores en tiempo real. Podemos usar un tamaño mediano para este registro, porque no esperamos tener muchas celdas de RAM (<1K).} TCPURamCellPtr = ^TCPURamCell; TCPURamCell = object private Fvalue : byte; //value of the memory function Getvalue: byte; procedure Setvalue(AValue: byte); public //General fields name : string; //Register name (for variables). used : TCPURamUsed; //Indicates if have been written. shared : boolean; //Used to share this register. state : TCPUCellState; //Status of the cell. property value : byte read Getvalue write Setvalue; property dvalue: byte read Fvalue write Fvalue; //Direct access to "Fvalue". function Avail : boolean; //RAM implemented to use in programs function Free : boolean; //RAM implemented and unused public //Debugging fields breakPnt : boolean; //Indicates if this cell have a Breakpoint {Be careful on the size of this record, because it's going to be multiplied by 64K} public //Information of position in source code. Used for debug rowSrc : word; //Row number colSrc : word; //Column number idFile : SmallInt; //Index to a file. No load the name to save space. rowGrid : word; //Used to include Grid information when debug. {Estos campos de cadena ocupan bastante espacio, aún cuado están en NULL. Si se quisiera optimizar el uso de RAM, se podría pensar en codificar, varios campos en una sola cadena.} topComment : string; //Comment on the top of the cell. sideComment: string; //Right comment to code end; TCPURam = array of TCPURamCell; TCPURamPtr = ^TCPURam; TCPURutExplorRAM = procedure(offs: word; regPtr: TCPURamCellPtr) of object; type { TCPUCore } {Abcestor of all 8 bits CPU cores} TCPUCore = class private function GetTokAddress(var str: string; delimiter: char): word; public //Limits {This variables are set just one time. So they work as constant.} CPUMAXRAM: dword; //Max virtual RAM used by the CPU public //General fields Model : string; //modelo de PIC frequen : integer; //frecuencia del reloj MaxFreq : integer; //Máxima frecuencia del reloj en Hz. //Propiedades que definen la arquitectura del CPU. MsjError: string; public //Execution control nClck : Int64; //Contador de ciclos de reloj CommStop: boolean; //Bandera para detener la ejecución OnExecutionMsg: procedure(message: string) of object; //Genera mensaje en ejecución public //PRG files generation minUsed : dword; //Dirección menor de la ROM usada maxUsed : dword; //Dirección mayor de la ROM usdas hexLines : TStringList; //Uusado para crear archivo *.hex public //Memories ram : TCPURam; //RAM memory iRam : integer; //puntero a la memoria RAM, para escribir cuando se ensambla o compila código. function DisassemblerAt(addr: word; out nBytesProc: byte; useVarName: boolean ): string; virtual; abstract; //Desensambla la instrucción actual public //RAM memory functions dataAddr1: integer; //Start address for Data variables (-1 if not used). Used too as flag. dataAddr2: integer; //End address for Data variables (-1 if not used) procedure ClearMemRAM; procedure DisableAllRAM; procedure SetStatRAM(i1, i2: word; status0: TCPUCellState); function SetStatRAMCom(strDef: string): boolean; function SetDataAddr(strDef: string): boolean; function HaveConsecRAM(const i, n: word; maxRam: dword): boolean; //Indica si hay "n" bytes libres procedure UseConsecRAM(const i, n: word); //Ocupa "n" bytes en la posición "i" procedure SetSharedUnused; procedure SetSharedUsed; public //RAM memory functions function UsedMemRAM: word; //devuelve el total de memoria ram usada public //RAM name managment function NameRAM(const addr: word): string; procedure SetNameRAM(const addr: word; const nam: string); //Fija nombre a una celda de RAM procedure AddNameRAM(const addr: word; const nam: string); //Agrega nombre a una celda de RAM public //Execution control procedure AddBreakpoint(aPC: word); procedure ToggleBreakpoint(aPC: word); procedure Exec(aPC: word); virtual; abstract; //Ejecuta la instrucción en la dirección indicada. procedure Exec; virtual; abstract; //Ejecuta instrucción actual procedure ExecTo(endAdd: word); virtual; abstract; //Ejecuta hasta cierta dirección procedure ExecStep; virtual; abstract; //Execute one instruction considering CALL as one instruction procedure ExecNCycles(nCyc: integer; out stopped: boolean); virtual; abstract; //Execute "n" cycles procedure Reset(hard: boolean); virtual; abstract; function ReadPC: dword; virtual; abstract; //Defined DWORD to cover the 18F PC register procedure WritePC(AValue: dword); virtual; abstract; public //Others // procedure addTopLabel(lbl: string); //Add a comment to the ASM code procedure addTopComm(comm: string; replace: boolean = true); //Add a comment to the ASM code procedure addSideComm(comm: string; before: boolean); //Add lateral comment to the ASM code procedure addPosInformation(rowSrc, colSrc: word; idFile: byte); public //Initialization constructor Create; virtual; destructor Destroy; override; end; implementation { TCPURamCell } function TCPURamCell.Getvalue: byte; begin Result := Fvalue; end; procedure TCPURamCell.Setvalue(AValue: byte); begin Fvalue := AValue; end; function TCPURamCell.Avail: boolean; {Indicates if the RAM cell is available for programmer.} begin Result := (state = cs_impleGPR); end; function TCPURamCell.Free: boolean; begin Result := (state = cs_impleGPR) and (used = ruUnused); end; function TCPUCore.GetTokAddress(var str: string; delimiter: char): word; {Extract a number (address) from a string and delete until de delimiter. If delimiter is #0, it's consider all the text. If fail update the variable "MsjError".} var p: SizeInt; n: Longint; begin if delimiter=#0 then begin //Until the end of line p := length(str) + 1; end else begin //Until delimiter p := pos(delimiter, str); if p = 0 then begin MsjError := 'Expected "'+delimiter+'".'; exit(0); end; end; //Have delimiter. Get number if not TryStrToInt('$'+copy(str,1,p-1), n) then begin MsjError := 'Wrong address.'; exit(0); end; delete(str, 1, p); //delete number and delimiter if n<0 then begin MsjError := 'Address cannot be negative.'; exit(0); end; if n>$FFFF then begin MsjError := 'Address cannot be greater than $FFFF.'; exit(0); end; exit(n); end; { TCPUCore } //RAM memory functions procedure TCPUCore.ClearMemRAM; {Clear content of RAM. Doesn't change the state.} var i: Integer; begin for i:=0 to high(ram) do begin ram[i].dvalue := $00; ram[i].used := ruUnused; ram[i].name :=''; ram[i].shared := false; ram[i].breakPnt := false; // ram[i].topLabel := ''; ram[i].sideComment:= ''; ram[i].topComment := ''; ram[i].idFile := -1; //Not initialized. {Don't change the implementation. Because "state" must be defined just once at the beginning } // ram[i].state := cs_impleGPR; end; end; procedure TCPUCore.DisableAllRAM; {Inicia el estado de toda la memoria RAM física definida em el Modelo. Solo debería usarse, para cuando se va a definir el hardware del dispositivo.} var i: word; begin for i:=0 to high(ram) do begin ram[i].state := cs_unimplem; end; end; procedure TCPUCore.SetStatRAM(i1, i2: word; status0: TCPUCellState); {Inicia el campo State, de la memoria. Permite definir el estado real de la memoria RAM. } var i: Integer; begin for i:=i1 to i2 do begin //verifica 1 a 1, por seguridad if i>CPUMAXRAM-1 then continue; //protection ram[i].state := status0; end; end; function TCPUCore.SetStatRAMCom(strDef: string): boolean; {Define the RAM state using a Definitions string. Definitions string have the format: , , ... Each command have the format: -: One example for Definitions string, is: '0000-01FF:SFR, 8000-FFFF:NIM' If error ocurrs, return FALSE, and the error messaje in MsjError. } var coms: TStringList; add1, add2: longint; state: TCPUCellState; com, str: String; begin Result := true; coms:= TStringList.Create; try coms.Delimiter := ','; coms.DelimitedText := strDef; for str in coms do begin com := UpCase(trim(str)); if com='' then continue; //Find Address1 add1 := GetTokAddress(com, '-'); if MsjError<>'' then begin MsjError := 'Memory definition syntax error: ' + MsjError; exit(false); end; //Find Address1 add2 := GetTokAddress(com, ':'); if MsjError<>'' then begin MsjError := 'Memory definition syntax error: ' + MsjError; exit(false); end; case copy(com,1,3) of 'SFR': state := cs_impleSFR; 'GPR': state := cs_impleGPR; 'NIM': state := cs_unimplem; else MsjError := 'Memory definition syntax error: Expected SFR, GPR or NIM'; exit(false); end; //Ya se tienen los parámetros, para definir la memoria SetStatRAM(add1, add2, state); end; finally coms.Destroy; end; end; function TCPUCore.SetDataAddr(strDef: string): boolean; {Define primary address to be used for allocating variables. Definitions string have the format: , , ... Each command have the format: -: One example for Definitions string, is: '00F0-00FF' If error ocurrs, return FALSE, and the error message in MsjError. } var add1, add2: longint; com: String; begin Result := true; com := UpCase(trim(strDef)); if com='' then begin dataAddr1 := -1; //Disable exit; end; //Find Address1 add1 := GetTokAddress(com, '-'); if MsjError<>'' then begin MsjError := 'Memory definition syntax error: ' + MsjError; exit(false); end; //Find Address1 add2 := GetTokAddress(com, #0); if MsjError<>'' then begin MsjError := 'Memory definition syntax error: ' + MsjError; exit(false); end; //Ya se tienen los parámetros, para definir la memoria dataAddr1 := add1; //Save dataAddr2 := add2; //Save end end; function TCPUCore.HaveConsecRAM(const i, n: word; maxRam: dword): boolean; {Indica si hay "n" bytes consecutivos libres en la posicióm "i", en RAM. La búsqueda se hace solo hasta la posición "maxRam"} var c: Integer; j: dword; begin Result := false; c := 0; j := i; while (j<=maxRam) and (cmaxRam then exit; //no hay más espacio //Si llega aquí es porque estaban libres los bloques Result := true; end; procedure TCPUCore.UseConsecRAM(const i, n: word); {Marca "n" bytes como usados en la posición de memoria "i", en la RAM. Debe haberse verificado previamente que los parámetros son válidos, porque aquí no se hará ninguna verificación.} var j: word; begin for j:=i to i+n-1 do begin ram[j].used := ruData; //todos los bits end; end; procedure TCPUCore.SetSharedUnused; {Set positions marked as "shared", as unused for to be used again.} var i: Integer; begin for i:=0 to high(ram) do begin if (ram[i].shared) and (ram[i].state = cs_impleGPR) then begin ram[i].used := ruUnused; end; end; end; procedure TCPUCore.SetSharedUsed; {Set positions marked as "shared", as used, for NOT to be used again.} var i: Integer; begin for i:=0 to high(ram) do begin if (ram[i].shared) and (ram[i].state = cs_impleGPR) then begin ram[i].used := ruData; //Set as used for variables end; end; end; function TCPUCore.UsedMemRAM: word; var i: Integer; begin Result := 0; for i:=$0000 to CPUMAXRAM-1 do begin if ram[i].used<>ruUnused then inc(Result); end; end; //RAM name managment function TCPUCore.NameRAM(const addr: word): string; {Devuelve el nombre de una celda de la memoria RAM.} begin Result := ram[addr].name; end; procedure TCPUCore.SetNameRAM(const addr: word; const nam: string ); {Escribe en el campo "name" de la RAM en la psoición indicada} begin ram[addr].name:=nam; end; procedure TCPUCore.AddNameRAM(const addr: word; const nam: string); {Escribe en el campo "name" de la RAM en la psoición indicada. Si ya existía un nombre, lo argega después de una coma.} begin if ram[addr].name = '' then begin ram[addr].name:=nam; end else begin ram[addr].name+=','+nam; end; end; //Execution control procedure TCPUCore.AddBreakpoint(aPC: word); //Agrega un punto de interrupción begin if aPC>=CPUMAXRAM then exit; ram[aPC].breakPnt := true; end; procedure TCPUCore.ToggleBreakpoint(aPC: word); //COnmuta el estado del Punto de Interrupción, en la posición indicada begin if aPC>=CPUMAXRAM then exit; ram[aPC].breakPnt := not ram[aPC].breakPnt; end; //procedure TCPUCore.addTopLabel(lbl: string); //begin // if iRam>=CPUMAXRAM then exit; // ram[iRam].topLabel := lbl; //end; procedure TCPUCore.addTopComm(comm: string; replace: boolean); {Agrega un comentario de línea al código en la posición de memoria actual} begin if iRam>=CPUMAXRAM then exit; if replace then begin ram[iRam].topComment := comm; end else begin ram[iRam].topComment := ram[iRam].topComment + comm; end; end; procedure TCPUCore.addSideComm(comm: string; before: boolean); {Agrega un comentario para que apareza al lado de la instrucción. "before" = TRUE -> Se debe llamar después de codificar la instrucción "before" = FALSE -> Se debe llamar antes de codificar la instrucción } begin if before then begin if iRam= 0 then exit; ram[iRam-1].sideComment+=comm; //se agrega al que pudiera haber end else begin if iRam= 0 then exit; ram[iRam].sideComment+=comm; //se agrega al que pudiera haber end; end; procedure TCPUCore.addPosInformation(rowSrc, colSrc: word; idFile: byte); {Agrega information de la posición en el codigo fuente, a la posición actual de la memoria RAM.} begin ram[iRam].rowSrc := rowSrc; ram[iRam].colSrc := colSrc; ram[iRam].idFile := idFile; end; //Initialization constructor TCPUCore.Create; begin dataAddr1 := -1; //Disable hexLines := TStringList.Create; MaxFreq := 10000000; //10MHz. frequen := 1000000; // 1MHz. end; destructor TCPUCore.Destroy; begin hexLines.Destroy; inherited Destroy; end; initialization end. //659