Skip to content
CPU 6502 Pascal Compiler/IDE/Debugger
Branch: 0.3
Clone or download
Latest commit 70323ac Apr 23, 2019
Type Name Latest commit message Commit time
Failed to load latest commit information.
_libraries Add files via upload Mar 6, 2019
syntax Add files via upload Sep 7, 2018
temp Add files via upload Sep 25, 2018
themes Add files via upload Sep 7, 2018
units Add files via upload Mar 2, 2019
P65Pas-linux Add files via upload Sep 7, 2018
P65Pas-linux.xml Add files via upload Sep 4, 2018
P65Pas-win32.xml Add files via upload Apr 5, 2019
P65Pas-win64.exe Add files via upload Mar 6, 2019
P65Pas-win64.xml Add files via upload Mar 6, 2019 Add files via upload Mar 31, 2019
cambios.txt Add files via upload Mar 3, 2019
version.txt Add files via upload Mar 1, 2019

P65Pas 0.3

Multi-platform Pascal cross-compiler for 6502 CPU.

P65Pas IDE

P65Pas is a Pascal compiler and IDE, written in Free Pascal, which generates binary and ASM code for the 6502 CPU.

No additional libraries or software required to compile. P65Pas generates the *.prg file directly. Additionally a BASIC Commodore program (POKE's) can be generated to charge the machine code.

P65Pas works with a simplified version of the Pascal language, that has been adapted to work with limited resources small devices.

Currently, it only supports basic types.

It includes a complete IDE/debugger/simulator to facilitate the development.

The P65Pas compiler includes optimization options so the code obtained is generally compact.


P65Pas doesn't need installation, and have not dependencies, except the commons of the operative system, where it's runnig.

To run, it's only needed to download the folder from GitHub. There is compiled binaries for Windows-64 version (P65Pas-win64.exe) and Ubuntu version (P65Pas-linux).

If it's required other platform, it need to be compiled from the source code.

When starting, P65Pas could generate warning messsages, if not needed folders exist.

Hello World

P65Pas can compile to a general 6502 system, and it's not possible to do a general "Hello World" program.

If we are going to compile for Commodore 64, we can use the Commodore64 unit and use the CHROUT procedure to send chars to screen:

{"Hello World" P65Pas program.}
program Hello;
uses Commodore64;
  asm RTS end 

This code can generate a PRG program, to send the message "Hello" on the screen of the Commodore 64.

The code "asm RTS end" line, generate a RTS assembler instruction in the program. It's needed because the PRG program is called with a JSR instruction.

Devices supported

Currently, only 6502 CPU is supported, but the compiler is designed to include some variants. Of course, compatible devices, like 6510, can be targeted too.

Support for differents systems (like Apple II, Atari 800XL, Commodore 64, ...) can be implemented using appropriate units.

Currently there is only a unit to support Commodore 64 system. So, to compile to Commodore 64 system, it's recommended use the unit: "Commodore64":

program anything;
uses Commodore64; 

The unit Commodore64 set the compiler to start compiling in the address $0801, and includes a starting code to run the program after loading the PRG.

Moreover, this unit includes some routines (like CHROUT and CHRIN) to control C64 screen and keyboard.


P65Pas includes an IDE integrated to the compiler, to help on developing the programs.

Some features of the IDE are:

• Cross-platform.

• Multiple editors windows.

• Syntax highlighting, code folding, word, and line highlighting for Pascal and ASM.

• Code completion, and templates for the common structures IF, REPEAT, WHILE, …

• Shows the assembler code and the resources used.

• Support for themes (skins).

• Code tools for completion and navigation.

• Check syntax in REAL TIME!!!.

• Several setting options.

• Translated to english, french, spanish and german.

Tito's Terminal

Tito's Terminal

Tito's Terminal


P65Pas includes a graphical debugger for ASM instructions:

Tito's Terminal

To control the execution, the following keys can be used:

F5 -> Set a breakpoint in the current position of the assembler code. F6 -> Reste the device. F7 -> Step by step into subroutine. F8 -> Step by step over subroutine. F9 -> Run the program in real time.

Language Reference

Program structure

program <Name>;  //optional
  //Units declarations

  //Constants declarations

  //Variables declarations

//<Procedures declaration>

  //Main program body

Unit structure

unit <name>;
  //units declaration
  //Constant declaration
  //Variable declaration

//Procedures declaration


  //units declaration
  //Constant declaration
  //Variable declaration

//Procedures implementation



Operator            Precedence
=================== ==========
 NOT, sign “-“         6
 *, DIV, MOD, AND      5
 +, -, OR, XOR         4
 =, <>, <, <=, >, >=   3
 := +=                 2


Type           Size
============== ==========
 boolean       1 bit
 byte          1 byte
 char          1 byte
 word          2 bytes
 dword         4 bytes

Numerical types are all unsigned.


Variables are defined with the VAR keyword:

  var1 : byte;
  large_name_variable: boolean;

Variables can be defined too, at an absolute memory address:

  PORTB: BYTE absolute $06;
  pin1: boolean; absolute $07;

Specific byte of a word, can be access using fields:

  word_var.Low := $ff;
  word_var.High := $ff;

Control structures

P65Pas doens't follow the common Pascal syntax. Instead, a new Modula-2, style syntax is implemented.

The common control structures have the following forms:

IF <condition> THEN 
  <block of code>

IF <condition> THEN 
  <block of code>
  <block of code>

IF <condition> THEN 
  <block of code>
ELSIF <condition> THEN 
  <block of code>
  <block of code>

WHILE <condition> DO
  <block of code>

  <block of code>
UNTIL <condition>;

FOR  <variable> := <start-value> TO <end-value> DO 
  <block of code>

System Functions

System functions are always available in code. They don't need to be defined or included in a unit.

============== =================================================
delay_ms()	   Generate a time delay in miliseconds, from 0 to 65536.
Inc()          Increase a variable.
Dec()          Decrease a varaible.
Exit()         Exit from a procedure or end the program.
Ord()          Convert a char to a byte.
Chr()          Convert a byte to a char.
Byte()         Convert an expression to a byte expression.
Word()         Convert an expression to a word expression.

Procedure and Functions

P65Pas use the Modula-2 syntax for procedures and functions:

Proedures are declared in the common Pascal syntax:

  procedure proc2(par1: byte);
    if par1 = 0 then 
      par1 := 5;

Functions are declared the same, but indicating the type to return:

procedure TheNext(par1: byte): byte;
  exit(par1 + 1);

The return value is indicated with the exit() instruction.

When using in procedures parameters, a REGISTER parameter can be included:

procedure QuickParameterProc(register regvar: byte);
  //Be carefull if put some code here
  PORTB := regvar;

REGISTER parameters are fast, because they use the W register, so only one REGISTER parameter can be used. As REGISTER parameter is stored in W register, any operation using the W register, could lose its value, so the first operation in a procedure, using a REGISTER parameter must be read this parameter.

ASM blocks

P65Pas have a complete support for inserting ASM code inside the Pascal source.

ASM blocks must be included between the delimiters ASM and END:

procedure DoSomething;
  x := 10;
    ;Load 3 to Acumulator 
    LDA #03

ASM blocks are not instructions, that's why they are not finished with ";". It lets the ASM block, to be included in almost any place of the source code, like a comment.

WARNING: Changing some register used by compiler, inside an ASM block, can generate errors in compilation or in the code compiled.

Absolute and relative Labels can be used too:

  JMP $+3	;Jump to next instruction
  ;infinite loop
  JMP label

Program variables can be accessed, using his common name:

 byte1: byte; 
 car1: char; 
 word1: word;
  //Low level clear
    INC byte1
    INC car1
    INC word1.Low

Constant can be accessed too, using the same way.

It's possible to use the directive ORG inside a ASM block, too:

    org $-2
  vbit := 1;

The address in ORG, can be absolute or relative.

WARNING: Changing the PC pointer with ORG, can generate errors in the compilation or in execution.


Directives are special instructions inserted in the source code that are interpreted and executed by the compiler when compiling the source code (in compilation time).

Directive Programming Language

Directives have their own programmig language. It's a simple and interpreted language (with instructions, variables, operators and conditional structures) what is different from Pascal.

Some features of this programming language are:

  • It's case insensitive, like Pascal is.
  • Instructions are contained in one single line and are delimited by {$ … }
  • It's not a typed language. Variables can change their type and value in execution and different type variables can be assigned.
  • Variables don't need to be defined before using.
  • There are only two types for variables: strings and numbers.


Variables are assigned with the instruction $SET:

{$SET x = 1}
{$SET y = 1 + x}
{$SET x = 'I'm now a string'}

$SET, is not a declaration, but an assignment. First time a variable is assigned, it's created.

Content of a variable, can be shown using instructions like $MSGBOX oo $INFO:

{$MSGBOX 'x is:' + x}

System Variables

There are some system variables, accessible from the directives language. They are:

{$MSGBOX PIC_MODEL} -> Shows the PIC model defined.

{$MSGBOX PIC_FREQUEN} -> Shows the Clock frequency.

{$MSGBOX PIC_MAXFREQ} -> Shows the Max Clock frequency for the device.

{$MSGBOX SYN_MODE} -> Shows the syntax Mode of the compiler.

(*) To see the complete list, check the User Manual.

List of Directives

The next directives are supported by P65Pas:


Indicates the compiler to compile to a Commodore 64 system.

In this mode when compiling at $0801, the compiler generates the instruction "SYS 2062" in the BASIC buffer, to run automatically the program after loading the PRG file.


Specify the clock frequency, in MHz or KHz. Example:


Frequency information is used for:

  • The compiler, when needed to generate delays.
  • The simulator, for Real Time simulation.

If delays are used in the program, only some frequencies are supported. They are:

1MHz, 2Mhz, 4Mhz, 8MHz, 10MHz, 12MHz, 16MHz or 20MHz.

If frequency is not specified, the default value is 4MHz.


Specify the syntax mode, used by the compiler. The allowed values are:

{$MODE P65Pas} -> Default mode. Use the new syntax for the control structures.

{$MODE PASCAL} -> Clasic Pascal mode. Use the common Pascal syntax for the control structures.


Shows a text message in the screen:

{$MSGBOX 'Hello World'} -> Shows the message 'Hello World' in the screen.

{$MSGBOX PIC_MODEL} -> Shows the system variable PIC_MODEL, that is the PIC model defined.

{$MSGBOX PIC_FREQUEN} -> Shows the Clock frequency.

{$MSGBOX 'clock=' + PIC_FREQUEN} -> Shows the message: "clock=8000000" (if the Frequency was set to 8MHz).


Shows a text message in the screen, with an error icon.


Shows a text message in the screen, with a warning icon.


Includes the contents of a external file, into de source code:

{$INCLUDE aaa.pas}
{$INCLUDE d:\temp\aaa.txt}
x := {$INCLUDE expression.txt};


Defines the name of the output binary file *.hex.

{$OUTPUTHEX myoutput.hex}  // Relative path
{$OUTPUTHEX d:\temp\myoutput.hex}  //Absolute path

When relative path is used, the file will be created in the same folder the Pascal program is.

If it's not defined the name of the *.hex file, it will be used the name of the program/unit compiled. So if the program is called "myprogram" (and the file is "myprogram.pas"), then the *.hex file will be "myprogram.hex".

Directive {$OUTPUTHEX}, can be placed in any part of the source code and can be used several times. If so, the output file will be the defined by the last directive.


Define symbols or macros

To define a symbol we can do:

{$define MY_SYMBOL}

Once defined, it can be tested using $IFDEF directive.

To define a macro we can do:

{$DEFINE macro=Hello}

Then we can expand a macro, in the code, using the way:


Following, there a sample code:

{$DEFINE value=$FF}
uses Commodore64;
var x: byte;
  x := {$value};


Set a value for a variable. If variables doesn't exist, it will be created.

{$SET pin_out='PORTB.0'}
uses Commodore64;
  {$pin_out} := 1;

Variables can be numbers or string.

Variables supports expresions:

{$SET a_var = 1 + 2 * another_var + 2 ^ sin(0.5)}

Unlike macros, variables values are solved when assigned. Macros values, are solved when macro is referenced.


This directives let us to define conditional compilation blocks:

Directive $IFDEF check the existence of some macro or variable and according to that, compile or not some blocks of code.

It has two forms:

{$IFDEF <identifier>} 
{$IFDEF <identifier>} 

The next code is an example of use:

uses Commodore64;
{$IFDEF MyPinOut}
  {$DEFINE MyPinOut=PORTB.1}
  {$MyPinOut} := 1;


This directive is the opposite version of $IFDEF.

uses Commodore64;
{$IFNDEF MyPinOut}
  {$DEFINE MyPinOut=PORTB.1}
  {$MyPinOut} := 1;


This directives let us to define conditional compilation blocks, using expressions:

Directive $IF evaluates an expression, and according to the result, compile or omit some blocks of code.

The common syntax is:

{$IF <expression>} 

A long way can be used too:

{$IF <expression>} 

The following code shows an example of use:

{$IF value>255}
var x: word;
var x: byte;

As there is not a boolean type, a boolean expression returns the number 1 when the expression is TRUE and 0 when the expression is FALSE.

On the other side, instruction {$IF} will consider as TRUE, any number different from 0, or any string not empty.


It's the opposite version of $IF.

{$IFNOT value>255}
var x: byte;
var x: word;


USed to define the initial state of RAM memory.

$CLEAR_STATE_RAM, set the state of all the RAM as unimplemented, clearing all previous setting.

It's used before of starting to define the RAM for a device, using the directives $SET_STATE_RAM and $SET_MAPPED_RAM.

P65Pas Limitations

• Only basic types are implemented: byte, char, boolean, word an dword(limited support).

• Cannot declare arrays or records.

• No recursion implemented, Because of the limited hardware resources.

• No float point implemented.

Some of these limitations must be solved in next versions.


P65Pas is a free software (GPL license) and it's opened for the collaboration of anyone who is interested.

There is still, much work for development or documentation, so any help will be appreciated.

Source Code

The source code of the compiler is in the folder /Source.

To compile P65Pas, it's needed to have the following libraries:

  • SynFacilUtils
  • MisUtils
  • MiConfig
  • P65Utils
  • t-Xpres
  • UtilsGrilla
  • ogEditGraf

All of them, must be availables on the GitHub. Check the versions used.

These libraries don't include package. They are only files in folders that need to be included when compiling P65Pas.

P65Pas has been compiled, using the version 1.8.0 of Lazarus. Tested in Windows, Ubuntu and Mac.

To have more information about the compiler, check the Technical Documentation (Only in spanish by now).


P65Pas is a new project and it's still in development and there are not dedicated libraries for the compiler.

You can’t perform that action at this time.