diff --git a/CPUCore.pas b/CPUCore.pas new file mode 100644 index 0000000..252dd38 --- /dev/null +++ b/CPUCore.pas @@ -0,0 +1,555 @@ +{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, LCLProc; +const + PIC_MAX_PINES = 64; //Max. number of pines for the package +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_impleGPR, //Implemented. Can be used. + cs_unimplem //Not implemented. + ); + + TCPUPinType = ( + pptVcc, //Alimentación + pptGND, //Tierra + pptControl,//Pin de control + pptPort, //Puerto Entrada/Salida + pptUnused //Pin no usado + ); + + { TCPUPin } + //Model for a phisycal pin of the PIC + TCPUPin = object + nam: string; //Eqtiueta o nombre + typ: TCPUPinType; //Tipo de pin + add: word; //Dirección en RAM + bit: byte; //Bit en RAM + function GetLabel: string; + end; + TCPUPinPtr = ^TCPUPin; + +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 + addr : word; //dirección física de memoria, en donde está la celda. + name : string; //Name of the register (or variable) + used : boolean; //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 AvailGPR: boolean; + public //Campos para deputación + 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. + {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.} + topLabel : string; //Label on the top of the cell. + 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, bnk: byte; regPtr: TCPURamCellPtr) of object; + +type + + { TCPUCore } + {Abcestor of all 8 bits PIC cores} + TCPUCore = class + public //Limits + {This variables are set just one time. So they work as constant.} + CPUMAXRAM : word; //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 + //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 + protected //Generation of HEX files + minUsed : word; //Dirección menor de la ROM usada + maxUsed : word; //Dirección mayor de la ROM usdas + hexLines : TStringList; //Uusado para crear archivo *.hex + public //Memories + ram : TCPURam; //memoria RAM + 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 + procedure ClearMemRAM; + procedure DisableAllRAM; + procedure SetStatRAM(i1, i2: word; status0: TCPUCellState); + function SetStatRAMCom(strDef: string): boolean; + function MapRAMtoPIN(strDef: string): boolean; + function HaveConsecRAM(const i, n: word; maxRam: word): 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 //Pins fields + Npins : byte; //Number of pins + pines : array[1..PIC_MAX_PINES] of TCPUPin; + procedure ResetPins; + procedure SetPin(pNumber: integer; pLabel: string; pType: TCPUPinType); + function SetPinName(strDef: string): boolean; + 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; //Ejecuta hasta cierta dirección + procedure Reset; 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.AvailGPR: boolean; +{Indica si el registro es una dirección disponible en la memoria RAM.} +begin + Result := (state = cs_impleGPR); +end; + +{ TCPUPin } +function TCPUPin.GetLabel: string; +{Devuelve una etiqueta para el pin} +begin + case typ of + pptUnused: Result := 'NC'; + else + Result := nam; + end; +end; +{ TCPUCore } +//RAM memory functions +procedure TCPUCore.ClearMemRAM; +{Limpia el contenido de la memoria} +var + i: Integer; +begin + for i:=0 to high(ram) do begin + ram[i].dvalue := $00; + ram[i].used := false; + ram[i].name:=''; + ram[i].shared := false; + ram[i].breakPnt := false; + ram[i].topLabel := ''; + ram[i].sideComment:= ''; + ram[i].topComment := ''; + ram[i].idFile := -1; //Indica no inicializado +// ram[i].state := cs_unimplem; //por defecto se considera no implementado + 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].addr := i; + ram[i].state := cs_unimplem; + end; + //Inicia estado de pines + for i:=1 to high(pines) do begin + pines[i].typ := pptUnused; + 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 el estado de la memoria RAM, usando una cadena de definición. +La cadena de definición, tiene el formato: +, , ... +Cada comando, tiene el formato: +-: +Un ejemplo de cadena de definición, es: + '000-01F:IMP, 020-07F:NIM' +Si hay error, devuelve FALSE, y el mensaje de error en MsjError. +} +var + coms: TStringList; + add1, add2: longint; + state: TCPUCellState; + staMem, 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; + if length(com)<>11 then begin + MsjError := 'Memory definition syntax error: Bad string size.'; + exit(false); + end; + if com[4] <> '-' then begin + MsjError := 'Memory definition syntax error: Expected "-".'; + exit(false); + end; + if com[8] <> ':' then begin + MsjError := 'Memory definition syntax error: Expected ":".'; + exit(false); + end; + //Debe tener el formato pedido + if not TryStrToInt('$'+copy(com,1,3), add1) then begin + MsjError := 'Memory definition syntax error: Wrong address.'; + exit(false); + end; + if not TryStrToInt('$'+copy(com,5,3), add2) then begin + MsjError := 'Memory definition syntax error: Wrong address.'; + exit(false); + end; + staMem := copy(com, 9, 3); + case staMem of + 'IMP': state := cs_impleGPR; + 'NIM': state := cs_unimplem; + else + MsjError := 'Memory definition syntax error: Expected SFR or GPR'; + 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.MapRAMtoPIN(strDef: string): boolean; +{Mapea puertos de memoria RAM a pines físicos del dispositivo. Útil para la simulación +La cadena de definición, tiene el formato: +:, , ... +Cada comando, tiene el formato: +:- +Un ejemplo de cadena de definición, es: + '005:0-17,1-18,2-1,3-2,4-3' +Si hay error, devuelve FALSE, y el mensaje de error en MsjError. +} +var + coms: TStringList; + add1, pin, bit: longint; + com, str, ramName: String; + pSep: SizeInt; +begin + Result := true; + //Obtiene dirección + if length(strDef) < 4 then begin + MsjError := 'Syntax error'; + exit(false); + end; + if strDef[4] <> ':' then begin + MsjError := 'Expected "<3-digits address>"'; + exit(false); + end; + if not TryStrToInt('$'+copy(strDef,1,3), add1) then begin + MsjError := 'Address format error.'; + exit(false); + end; + delete(strDef, 1, 4); //quita la dirección + //Obtiene lista de asociaciones + coms:= TStringList.Create; + try + coms.Delimiter := ','; + coms.DelimitedText := strDef; + for str in coms do begin + com := UpCase(trim(str)); //asociación + if com='' then continue; + pSep := pos('-',com); //Posición de separador + if pSep = 0 then begin + MsjError := 'Expected "-".'; + exit(false); + end; + //Debe tener el formato pedido +// debugln(com); + if not TryStrToInt(copy(com,1,pSep-1), bit) then begin + MsjError := 'Error in bit number.'; + exit(false); + end; + if not TryStrToInt(copy(com,pSep+1,length(com)), pin) then begin + MsjError := 'Error in pin number.'; + exit(false); + end; + if (pin<0) or (pin>PIC_MAX_PINES) then begin + MsjError := 'Pin number out of range.'; + exit(false); + end; + if pin>Npins then begin + MsjError := 'Pin number out of range, for this device.'; + exit(false); + end; + //Ya se tiene el BIT y el PIN. Configura datos del PIN + pines[pin].add := add1; + pines[pin].bit := bit; + pines[pin].typ := pptPort; + ramName := ram[add1].name; + if ramName='' then ramName := 'PORT'; + pines[pin].nam := ramName + '.' + IntToStr(bit); //Nombre por defecto + end; + finally + coms.Destroy; + end; +end; +procedure TCPUCore.ResetPins; +{Reset the pins of the device.} +var + i: byte; +begin + for i:=1 to Npins do begin + pines[i].nam := ' '; + pines[i].typ := pptUnused; + end; +end; +procedure TCPUCore.SetPin(pNumber: integer; pLabel: string; pType: TCPUPinType); +begin + if pNumber>PIC_MAX_PINES then exit; + pines[pNumber].nam := pLabel; + pines[pNumber].typ := pType; +end; +function TCPUCore.SetPinName(strDef: string): boolean; +{Define the name for a specified Pin of the microcontroller, using a string. +"strDef" have the format: +: +On error this function return FALSE, and the error menssage in MsjError. +} +var + com, pinName: String; + pNumber: integer; + pcol: SizeInt; +begin + com := UpCase(trim(strDef)); + if com='' then exit; + pcol := Pos(':', strDef); + if pcol=0 then begin + MsjError := 'SetPinName: Expected ":".'; + exit(false); + end; + //"com" must have the correct format + if not TryStrToInt( copy(com, 1, pcol-1) , pNumber) then begin + MsjError := 'SetPinName: Wrong Pin Number.'; + exit(false); + end; + pinName :=copy(com, pcol+1, 32); //limited to 32 + SetPin(pNumber, pinName, pptControl); +end; +function TCPUCore.HaveConsecRAM(const i, n: word; maxRam: word): 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: word; +begin + Result := false; + c := 0; + j := i; + while (j<=maxRam) and (c cs_impleGPR) or (ram[j].used) then exit; + inc(c); //verifica siguiente + inc(j); + end; + if j>maxRam 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 := true; //todos los bits + end; +end; +procedure TCPUCore.SetSharedUnused; +{Marca las posiciones que estén en "shared", como no usadas, para que se puedan +usar nuevamente.} +var + i: Integer; +begin + for i:=0 to high(ram) do begin + if (ram[i].state = cs_impleGPR) and (ram[i].shared) then begin + ram[i].used := false; //pone en cero + end; + end; +end; +procedure TCPUCore.SetSharedUsed; +{Marca las posiciones que estén en "shared", como usadas, para que no se puedan +usar nuevamente.} +var + i: Integer; +begin + for i:=0 to high(ram) do begin + if (ram[i].state = cs_impleGPR) and (ram[i].shared) then begin + ram[i].used := true; //pone en uno + 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 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 + 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 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 + hexLines := TStringList.Create; + frequen := 4000000; //4MHz +end; +destructor TCPUCore.Destroy; +begin + hexLines.Destroy; + inherited Destroy; +end; + +initialization +end. +//659 diff --git a/Cambios.txt b/Cambios.txt index 07d4044..3f7c6e2 100644 --- a/Cambios.txt +++ b/Cambios.txt @@ -1,5 +1,10 @@ +0.1 +=== +Se eliminan algunos campos no usados, porque esta librera se ha creado a partir de la librera +PicUtils que contiene ms detalle en cuanto al hardware. +Se corrige errores en el ejemplo de ensamblador. + 0.0 === - Primera versin compilable de la librera. An queda pendiente la implementacin de la ejecucin de las instrucciones, y revisar todo la librera para quitar elementos no usados ya que esta librara se ha creado a partir de la librera PicUtils. diff --git a/MiniAssembler/project1.exe.hex b/MiniAssembler/project1.exe.hex new file mode 100644 index 0000000..269f46c --- /dev/null +++ b/MiniAssembler/project1.exe.hex @@ -0,0 +1,4 @@ +poke 0,165 +poke 1,0 +poke 2,133 +poke 3,0 diff --git a/MiniAssembler/project1.lpi b/MiniAssembler/project1.lpi new file mode 100644 index 0000000..a32364f --- /dev/null +++ b/MiniAssembler/project1.lpi @@ -0,0 +1,80 @@ + + + + + + + + + + <ResourceType Value="res"/> + <UseXPManifest Value="True"/> + <Icon Value="0"/> + </General> + <i18n> + <EnableI18N LFM="False"/> + </i18n> + <BuildModes Count="1"> + <Item1 Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + </PublishOptions> + <RunParams> + <local> + <FormatVersion Value="1"/> + </local> + </RunParams> + <RequiredPackages Count="1"> + <Item1> + <PackageName Value="LCL"/> + </Item1> + </RequiredPackages> + <Units Count="2"> + <Unit0> + <Filename Value="project1.lpr"/> + <IsPartOfProject Value="True"/> + </Unit0> + <Unit1> + <Filename Value="unit1.pas"/> + <IsPartOfProject Value="True"/> + <ComponentName Value="Form1"/> + <HasResources Value="True"/> + <ResourceBaseClass Value="Form"/> + <UnitName Value="Unit1"/> + </Unit1> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="project1"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <OtherUnitFiles Value=".."/> + <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Linking> + <Options> + <Win32> + <GraphicApplication Value="True"/> + </Win32> + </Options> + </Linking> + </CompilerOptions> + <Debugging> + <Exceptions Count="3"> + <Item1> + <Name Value="EAbort"/> + </Item1> + <Item2> + <Name Value="ECodetoolError"/> + </Item2> + <Item3> + <Name Value="EFOpenError"/> + </Item3> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/MiniAssembler/project1.lpr b/MiniAssembler/project1.lpr new file mode 100644 index 0000000..2500258 --- /dev/null +++ b/MiniAssembler/project1.lpr @@ -0,0 +1,21 @@ +program project1; + +{$mode objfpc}{$H+} + +uses + {$IFDEF UNIX}{$IFDEF UseCThreads} + cthreads, + {$ENDIF}{$ENDIF} + Interfaces, // this includes the LCL widgetset + Forms, Unit1 + { you can add units after this }; + +{$R *.res} + +begin + RequireDerivedFormResource := True; + Application.Initialize; + Application.CreateForm(TForm1, Form1); + Application.Run; +end. + diff --git a/MiniAssembler/project1.lps b/MiniAssembler/project1.lps new file mode 100644 index 0000000..46cf53e --- /dev/null +++ b/MiniAssembler/project1.lps @@ -0,0 +1,148 @@ +<?xml version="1.0" encoding="UTF-8"?> +<CONFIG> + <ProjectSession> + <PathDelim Value="\"/> + <Version Value="10"/> + <BuildModes Active="Default"/> + <Units Count="5"> + <Unit0> + <Filename Value="project1.lpr"/> + <IsPartOfProject Value="True"/> + <EditorIndex Value="-1"/> + <WindowIndex Value="-1"/> + <TopLine Value="-1"/> + <CursorPos X="-1" Y="-1"/> + <UsageCount Value="60"/> + </Unit0> + <Unit1> + <Filename Value="unit1.pas"/> + <IsPartOfProject Value="True"/> + <ComponentName Value="Form1"/> + <HasResources Value="True"/> + <ResourceBaseClass Value="Form"/> + <UnitName Value="Unit1"/> + <TopLine Value="152"/> + <CursorPos Y="171"/> + <UsageCount Value="60"/> + <Loaded Value="True"/> + <LoadedDesigner Value="True"/> + </Unit1> + <Unit2> + <Filename Value="..\p6502utils.pas"/> + <UnitName Value="P6502utils"/> + <IsVisibleTab Value="True"/> + <EditorIndex Value="1"/> + <FoldState Value=" TL00C1414113 T4H01 T5J0{W11]ACjEK5 T7jPJC T8jQ26132 T3m8051]ZD2]E0nFD7I2K T6n601D THnEF%2?"/> + <UsageCount Value="32"/> + <Bookmarks Count="1"> + <Item0 Y="1211" ID="1"/> + </Bookmarks> + <Loaded Value="True"/> + </Unit2> + <Unit3> + <Filename Value="..\CPUCore.pas"/> + <EditorIndex Value="2"/> + <TopLine Value="472"/> + <CursorPos X="6" Y="486"/> + <FoldState Value=" TL0040211193 T4801 T5A[64CED8 T7dC6 T8iE230122145 T3kX0331]mHl8B4]i6lI01A T0leR3C pjAjYN3857 TDmZ2e]ZEnU]+'"/> + <UsageCount Value="25"/> + <Bookmarks Count="1"> + <Item0 Y="117" ID="3"/> + </Bookmarks> + <Loaded Value="True"/> + </Unit3> + <Unit4> + <Filename Value="C:\lazarus\lcl\include\control.inc"/> + <EditorIndex Value="-1"/> + <TopLine Value="2838"/> + <CursorPos Y="2861"/> + <UsageCount Value="10"/> + </Unit4> + </Units> + <JumpHistory Count="22" HistoryIndex="21"> + <Position1> + <Filename Value="..\p6502utils.pas"/> + <Caret Line="1223" Column="3" TopLine="1169"/> + </Position1> + <Position2> + <Filename Value="..\p6502utils.pas"/> + </Position2> + <Position3> + <Filename Value="..\p6502utils.pas"/> + <Caret Line="208" Column="30" TopLine="167"/> + </Position3> + <Position4> + <Filename Value="..\p6502utils.pas"/> + <Caret Line="363" Column="17" TopLine="254"/> + </Position4> + <Position5> + <Filename Value="..\p6502utils.pas"/> + </Position5> + <Position6> + <Filename Value="..\p6502utils.pas"/> + <Caret Line="226" TopLine="211"/> + </Position6> + <Position7> + <Filename Value="..\p6502utils.pas"/> + </Position7> + <Position8> + <Filename Value="..\p6502utils.pas"/> + <Caret Line="259" Column="21" TopLine="218"/> + </Position8> + <Position9> + <Filename Value="..\p6502utils.pas"/> + <Caret Line="355" Column="65" TopLine="252"/> + </Position9> + <Position10> + <Filename Value="..\p6502utils.pas"/> + <Caret Line="368" Column="5" TopLine="265"/> + </Position10> + <Position11> + <Filename Value="..\p6502utils.pas"/> + <Caret Line="373" Column="32" TopLine="265"/> + </Position11> + <Position12> + <Filename Value="..\p6502utils.pas"/> + <Caret Line="1060" Column="49" TopLine="963"/> + </Position12> + <Position13> + <Filename Value="..\p6502utils.pas"/> + <Caret Line="1085" Column="49" TopLine="1064"/> + </Position13> + <Position14> + <Filename Value="..\p6502utils.pas"/> + </Position14> + <Position15> + <Filename Value="..\p6502utils.pas"/> + <Caret Line="370" Column="8" TopLine="354"/> + </Position15> + <Position16> + <Filename Value="..\p6502utils.pas"/> + <Caret Line="369" Column="28" TopLine="354"/> + </Position16> + <Position17> + <Filename Value="..\p6502utils.pas"/> + <Caret Line="1148" TopLine="1141"/> + </Position17> + <Position18> + <Filename Value="..\p6502utils.pas"/> + <Caret Line="207" Column="33" TopLine="180"/> + </Position18> + <Position19> + <Filename Value="..\p6502utils.pas"/> + </Position19> + <Position20> + <Filename Value="..\p6502utils.pas"/> + <Caret Line="1077" Column="62" TopLine="957"/> + </Position20> + <Position21> + <Filename Value="..\p6502utils.pas"/> + <Caret Line="1446" Column="12" TopLine="1155"/> + </Position21> + <Position22> + <Filename Value="..\CPUCore.pas"/> + <Caret Line="155" Column="6" TopLine="121"/> + </Position22> + </JumpHistory> + </ProjectSession> +</CONFIG> diff --git a/MiniAssembler/project1.res b/MiniAssembler/project1.res new file mode 100644 index 0000000..877868c Binary files /dev/null and b/MiniAssembler/project1.res differ diff --git a/MiniAssembler/unit1.lfm b/MiniAssembler/unit1.lfm new file mode 100644 index 0000000..637ca93 --- /dev/null +++ b/MiniAssembler/unit1.lfm @@ -0,0 +1,55 @@ +object Form1: TForm1 + Left = 265 + Height = 368 + Top = 159 + Width = 530 + Caption = 'Form1' + ClientHeight = 368 + ClientWidth = 530 + OnCreate = FormCreate + OnDestroy = FormDestroy + LCLVersion = '1.8.0.6' + object Memo1: TMemo + Left = 10 + Height = 312 + Top = 40 + Width = 190 + Lines.Strings = ( + 'LDA' + 'STA $10' + ) + TabOrder = 0 + end + object Button1: TButton + Left = 216 + Height = 25 + Top = 40 + Width = 88 + Caption = 'Assemble>>' + OnClick = Button1Click + TabOrder = 1 + end + object Memo2: TMemo + Left = 320 + Height = 312 + Top = 40 + Width = 190 + TabOrder = 2 + end + object Label1: TLabel + Left = 10 + Height = 15 + Top = 10 + Width = 31 + Caption = 'CODE' + ParentColor = False + end + object Label2: TLabel + Left = 328 + Height = 15 + Top = 10 + Width = 46 + Caption = 'HEX FILE' + ParentColor = False + end +end diff --git a/MiniAssembler/unit1.pas b/MiniAssembler/unit1.pas new file mode 100644 index 0000000..7ca83f1 --- /dev/null +++ b/MiniAssembler/unit1.pas @@ -0,0 +1,186 @@ +{Sample of how to create a very basic assembler tool, using the unit pic16utils.} +unit Unit1; +{$mode objfpc}{$H+} + +interface +uses + Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, + P6502utils; + +type + { TForm1 } + + TForm1 = class(TForm) + Button1: TButton; + Label1: TLabel; + Label2: TLabel; + Memo1: TMemo; + Memo2: TMemo; + procedure Button1Click(Sender: TObject); + procedure FormCreate(Sender: TObject); + procedure FormDestroy(Sender: TObject); + private + pic: TP6502; + function CaptureComma(var lin: string): boolean; + function ExtractNumber(var lin: string; out num: word): boolean; + function ExtractString(var lin: string; var str: string): boolean; + end; + +var + Form1: TForm1; + +implementation +{$R *.lfm} +{ TForm1 } + +function TForm1.ExtractString(var lin: string; var str: string): boolean; +var + tmp: String; + i: Integer; +begin + Result := true; + lin := trim(lin); //trim + if lin='' then begin + Application.MessageBox('Expected identifier.',''); + Result := false; + exit; + end; + tmp := ''; + i:=1; + while lin[i] in ['a'..'z','A'..'Z'] do begin + tmp += lin[i]; + inc(i); + end; + lin := copy(lin,i,100); + lin := trim(lin); //trim + str := tmp; + if str = '' then begin + //No string found + exit(false); + end; +end; +function TForm1.ExtractNumber(var lin: string; out num: word): boolean; +var + tmp: String; + i: Integer; +begin + Result := true; + lin := trim(lin); //trim + if lin='' then begin + Application.MessageBox('Expected number.',''); + Result := false; + exit; + end; + tmp := ''; + i:=1; + while lin[i] in ['$','0'..'9','x','X'] do begin + tmp += lin[i]; + inc(i); + end; + lin := copy(lin,i,100); + lin := trim(lin); //trim + if LowerCase( copy(tmp,1,2)) = '0x' then + num := StrToInt('$' + copy(tmp,3,100)) + else + num := StrToInt(tmp); +end; +function TForm1.CaptureComma(var lin: string): boolean; +begin + Result := true; + lin := trim(lin); //trim + if lin='' then begin + Application.MessageBox('Expected comma.',''); + Result := false; + exit; + end; + if lin[1]<>',' then begin + Application.MessageBox('Expected comma.',''); + Result := false; + exit; + end; + lin := copy(lin,2,100); + lin := trim(lin); //trim +end; + +procedure TForm1.Button1Click(Sender: TObject); +var + l: String; + idInst: TP6502Inst; + Inst: String; + stx, lin, Par: String; + n: word; +begin + pic.iRam:=0; //Start to code at $0000 + pic.MsjError:=''; //Clear error + for lin in Memo1.Lines do begin + l := lin; //crea copia para modificar + if trim(l) = '' then continue; + if not ExtractString(l, Inst) then begin //extract mnemonic + Application.MessageBox('Syntax Error',''); + exit; + end; + //Find mnemonic, and parameters + idInst := pic.FindOpcode(Inst); + if idInst = i_Inval then begin + Application.MessageBox(PChar('Invalid Opcode: '+ Inst),''); + exit; + end; + //Extract parameters + if l = '' then begin + //No parameters. Must be Implicit + pic.codAsmFD(idInst, aImplicit , 0); + if pic.MsjError<>'' then begin + Application.MessageBox(PChar(lin + ':' + pic.MsjError),''); + exit; + end; + end else if ExtractString(l, Par) then begin //extract mnemonic + //It's a string + if Par = 'A' then begin + //Accumulator mode + pic.codAsmFD(idInst, aAcumulat , 0); + if pic.MsjError<>'' then begin + Application.MessageBox(PChar(lin + ':' + pic.MsjError),''); + exit; + end; + end else begin + Application.MessageBox(PChar(lin + ': Syntax error' ),''); + end; + end else if ExtractNumber(l, n) then begin + //There is a number + if n<256 then begin + //Zero page. Although could be ,X + pic.codAsmFD(idInst, aZeroPage , 0); + if pic.MsjError<>'' then begin + Application.MessageBox(PChar(lin + ':' + pic.MsjError),''); + exit; + end; + end else begin + //Absolute. Although could be ,X + pic.codAsmFD(idInst, aAbsolute , 0); + if pic.MsjError<>'' then begin + Application.MessageBox(PChar(lin + ':' + pic.MsjError),''); + exit; + end; + end; + end else begin + //Not a string, nor a number, nor empty + Application.MessageBox('Syntax Error',''); + exit; + end; + end; + pic.GenHex(Application.ExeName + '.hex'); + Memo2.Lines.LoadFromFile(Application.ExeName + '.hex'); +end; + +procedure TForm1.FormCreate(Sender: TObject); +begin + pic := TP6502.Create; +end; + +procedure TForm1.FormDestroy(Sender: TObject); +begin + pic.Destroy; +end; + +end. + diff --git a/P6502utils.pas b/P6502utils.pas new file mode 100644 index 0000000..b799179 --- /dev/null +++ b/P6502utils.pas @@ -0,0 +1,1446 @@ +{ +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 = ( + 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 + 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 + public //Métodos para codificar instrucciones de acuerdo a la sintaxis + procedure useRAM; + procedure codAsmFD(const inst: TP6502Inst; addMode: TP6502AddMode; param: word); + function codInsert(iRam0, 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 iRam. Si hay error, +actualiza el campo "MsjError"} +begin + //Protección de desborde + if iRam >= CPUMAXRAM then begin + MsjError := 'RAM 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 + 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 + 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(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; +//procedure TP6502.BTFSC_sw_BTFSS(iRam0: integer); +//{Exchange instruction i_BTFSC to i_BTFSS, or viceversa, in the specified address.} +//begin +// //Solo necesita cambiar el bit apropiado +// ram[iRam0].value := ram[iRam0].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 + 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 +} +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 + aImplicit: 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 RAM. +"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 := CPUMAXRAM; //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. + 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 RAM 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 := CPUMAXRAM; + for i:=0 to CPUMAXRAM-1 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 CPUMAXRAM - 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 CPUMAXRAM - 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 CPUMAXRAM - 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 > CPUMAXRAM then exit(false); //excede límite + exit(true); +end; +procedure TP6502.GenHex(hexFile: string; ConfigWord: integer = -1); +{Genera el archivo *.hex, a partir de los datos almacenados en la memoria RAM. +Actualiza los campos, minUsed y maxUsed.} +var + iHex: word; //Índice para explorar + addr: word; //Dirección de inicio + i: Integer; + +begin + hexLines.Clear; //Se usará la lista hexLines + //Prepara extracción de datos + minUsed := CPUMAXRAM; + maxUsed := 0; + iHex := 0; + //Busca dirección de inicio usada + for i := 0 to CPUMAXRAM-1 do begin + if ram[i].used then begin + if i<minUsed then minUsed := i; + if i>maxUsed then maxUsed := i; + hexLines.Add('poke ' + IntToStr(i) + ',' + IntToStr(ram[i].value)); + end; + end; + //hexLines.Add('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; + //Default hardware settings + CPUMAXRAM := 4096; //Máx RAM memory + SetLength(ram, CPUMAXRAM); + //inicia una configuración común + ClearMemRAM; + SetStatRAM($020, $04F, cs_impleGPR); + + //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 + /////////////////////////////////////////////////////////////////// + + ////////////////////////////// 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(aImplicit,$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(aImplicit,$18,1,2,''); + PIC16InstName[i_CLD].name := 'CLD'; //Clear Decimal Mode + PIC16InstName[i_CLD].AddAddressMode(aImplicit,$D8,1,2,''); + PIC16InstName[i_CLI].name := 'CLI'; //Clear Interrupt Disable Bit + PIC16InstName[i_CLI].AddAddressMode(aImplicit,$58,1,2,''); + PIC16InstName[i_CLV].name := 'CLV'; //Clear Overflow Flag + PIC16InstName[i_CLV].AddAddressMode(aImplicit,$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(aImplicit,$CA,1,2,''); + PIC16InstName[i_DEY].name := 'DEY'; //Decrement Index Y by One + PIC16InstName[i_DEY].AddAddressMode(aImplicit,$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(aImplicit,$E8,1,2,''); + PIC16InstName[i_INY].name := 'INY'; //Increment Index Y by One + PIC16InstName[i_INY].AddAddressMode(aImplicit,$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(aImplicit,$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(aImplicit,$48,1,3,''); + PIC16InstName[i_PHP].name := 'PHP'; //Push Processor Status on Stack + PIC16InstName[i_PHP].AddAddressMode(aImplicit,$08,1,3,''); + PIC16InstName[i_PLA].name := 'PLA'; //Pull Accumulator from Stack + PIC16InstName[i_PLA].AddAddressMode(aImplicit,$68,1,4,''); + PIC16InstName[i_PLP].name := 'PLP'; //Pull Processor Status fromStack + PIC16InstName[i_PLP].AddAddressMode(aImplicit,$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(aImplicit,$40,1,6,''); + PIC16InstName[i_RTS].name := 'RTS'; //Return from Subroutine + PIC16InstName[i_RTS].AddAddressMode(aImplicit,$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(aImplicit,$38,1,2,''); + PIC16InstName[i_SED].name := 'SED'; //Set Decimal Flag + PIC16InstName[i_SED].AddAddressMode(aImplicit,$F8,1,2,''); + PIC16InstName[i_SEI].name := 'SEI'; //Set Interrupt Disable Status + PIC16InstName[i_SEI].AddAddressMode(aImplicit,$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(aImplicit,$AA,1,2,''); + PIC16InstName[i_TAY].name := 'TAY'; //Transfer Accumulator to IndexY + PIC16InstName[i_TAY].AddAddressMode(aImplicit,$A8,1,2,''); + PIC16InstName[i_TSX].name := 'TSX'; //Transfer Stack Pointer toIndex X + PIC16InstName[i_TSX].AddAddressMode(aImplicit,$BA,1,2,''); + PIC16InstName[i_TXA].name := 'TXA'; //Transfer Index X to Accumulator + PIC16InstName[i_TXA].AddAddressMode(aImplicit,$8A,1,2,''); + PIC16InstName[i_TXS].name := 'TXS'; //Transfer Index X to StackRegister + PIC16InstName[i_TXS].AddAddressMode(aImplicit,$9A,1,2,''); + PIC16InstName[i_TYA].name := 'TYA'; //Transfer Index Y to Accumulator + PIC16InstName[i_TYA].AddAddressMode(aImplicit,$98,1,2,''); + + PIC16InstName[i_Inval].name := 'Inval'; + + +end; +initialization + InitTables; +end. //1550