PDA

View Full Version : Pas2C64



paul_nicholls
21-01-2011, 03:46 AM
Hey all,
Just for fun, I am attempting to make a 'compiler' (called Pas2C64) that will compile a simple Pascal-like language into C64 (6510 processor) assembly instructions.

The assembly instructions will assembled by Win2C64 (http://www.aartbik.com/MISC/c64.html), a windows cross-assembler that creates T64 (tape format) programs that can be run by a C64 emulator. There are Linux/Mac versions too apparently on request...

I knew that there were a bunch of cross compilers (like cc65 (http://www.cc65.org/), a 6502 C compiler) that generate C64 programs using a C/C++ language, but I wanted good old Pascal damn-it! LOL

I have started creating a TCodeGenerator_C64 class which I have been directly testing (no Pascal -> ML yet..):

Input:

cg.WriteMainProgramStart(49152);

cg.WriteComment('Copy the background color to the border');

cg.LoadReg_Mem(regA,$D021);
cg.StoreReg(regA,$D020);

cg.OpCode(opRTS);

Output:

.org $C000
main
;Copy the background color to the border
LDA $D021
STA $D020
RTS


I can then assemble the code using Win2C64, and run it on the WinVICE C64 emulator doing this:


SYS 49152

To save time for myself (or others), I can also add in a Basic loader at the beginning of the code by doing this:


cg.WriteBasicLoader;
cg.WriteMainProgramStart;

cg.WriteComment('Copy the background color to the border');

cg.LoadReg_Mem(regA,$D021);
cg.StoreReg(regA,$D020);

cg.OpCode(opRTS);


which produces this:


.org $0800 ; start at BASIC
.byte $00 $0c $08 $0a $00 $9e $20 $32 ; encode SYS 2064
.byte $30 $36 $34 $00 $00 $00 $00 $00 ; as BASIC line

Lab2064
JMP main
;Copy the background color to the border
LDA $D021
STA $D020
RTS

This means that when the program loads in WinVICE, it automatically runs due to the line of basic code that does the system call to the machine code program :)

This should be an interesting little diversion for me LOL

cheers,
Paul

VilleK
21-01-2011, 08:23 AM
Good idea Paul :)
I enjoy these kind of retro-projects and I will follow your progress.

chronozphere
21-01-2011, 08:57 AM
Sounds interesting. Making a compiler is also on my todo-list. :)

paul_nicholls
21-01-2011, 12:04 PM
Well, I have gotten a bit further, am actually doing some parsing now...

Now I can do this:


WriteMemB(53248,5);

WriteMemB($A000,201);

WriteMemB(32768,%1010);

CopyMemB(53248,$D022)

and get this:



.org $0800 ; start at BASIC
.byte $00 $0c $08 $0a $00 $9e $20 $32 ; encode SYS 2064
.byte $30 $36 $34 $00 $00 $00 $00 $00 ; as BASIC line

Lab2064
JMP main
main
LDA #$05
STA $D000

LDA #$C9
STA $A000

LDA #$0A
STA $8000

LDA $D000
STA $D022
RTS


I added in some blank lines in the assembler output to break up the commands output so you can see it more clearly :)

It even checks that the address is not larger than $FFFF, and that the value is not larger than $FF for the commands...

It's a start! ;)

cheers,
Paul

farcodev
24-01-2011, 02:47 AM
as usual paul you make incredible work ! :)

paul_nicholls
24-01-2011, 03:11 AM
as usual paul you make incredible work ! :)

:-[ thanks mate :)

You are very good yourself, looking at your game screenshots!

cheers,
Paul

Ñuño Martínez
24-01-2011, 12:36 PM
May be you remember I started a compiler for Z-80 (here (http://z80-pascal.sf.net/)). Currently it's in stand by (as most of my current projects) but it has a complete parser and is able to compile "ASM".

I'll take a look to your project to steal some code. ;)

paul_nicholls
24-01-2011, 07:35 PM
LOL! I will have to release some code first ;)

cheers,
Paul

paul_nicholls
08-02-2011, 11:27 AM
Since I have added a WriteLn() command (for strings only ATM) to pas2c64, I decided to make a "hello world" example! LOL

http://img251.imageshack.us/img251/3285/pas2c64helloworld.th.png (http://img251.imageshack.us/i/pas2c64helloworld.png/)

I skipped the basic loader to save some vertical space...

It might not be the most exciting code ever, but it works <G>
cheers,
Paul

Darthman
08-02-2011, 11:53 AM
hmmmmm.... nostalgia.. (remember of my C64 in faк far past ^_^)

Great, keep it going !

code_glitch
08-02-2011, 09:03 PM
Hmm is that PDN in the background? 1 word man: Sweet. Not M$ paint or something paid for but open source goodness I miss very much as a native app in linux, yes Pinta and others have their benefits, but its never the same is it?

Anyway, nice work. I especially like that editor. Keep it up dude ;)

paul_nicholls
08-02-2011, 09:48 PM
Hmm is that PDN in the background? 1 word man: Sweet. Not M$ paint or something paid for but open source goodness I miss very much as a native app in linux, yes Pinta and others have their benefits, but its never the same is it?

Anyway, nice work. I especially like that editor. Keep it up dude ;)

LOL Yeah, it is Paint.NET in the background - I used it for saving the image ;)

I have made the editor using D2010, and the TSynEdit component so I could get syntax highlighting, etc. very sweet :)

cheers,
Paul

paul_nicholls
08-02-2011, 09:59 PM
hmmmmm.... nostalgia.. (remember of my C64 in faк far past ^_^)

Great, keep it going !

<Homer Simpson accent>MMMMMMMMMMMMM...............NOSTALGIA........</Homer Simpson accent>

haha!

Thanks for the support :)

cheers,
Paul

deathshadow
14-02-2011, 11:42 AM
Cool to see someone else targeting retro hardware... I just finished a DOS game targeting a 128k 4.77mhz 8088 as the minimum spec... though I've been also working in DEFT Pascal on the TRS-80 Color Computer.

Was there no original pascal compiler for the C64 in the first place? Admittedly, a modern optimizing compiler meant for the target system would be a huge step up (just compare TP7 to TP3) but surely there must have been SOMETHING.

I mean, on the Coco we had three -- OS/9 Pascal (which sucked but is what most people used), DEFT Pascal (the only one that made standalone .bin files you could cloadm or loadm and exec while having access to all 64k of memory if present) and StarPAS (which was just a p-code interpreter)... surely there must have been something for the C=64.

Don't suppose you are planning on adding VIC=20 support to that? Just thinking I've still got one here. (along with my 64K coco 1 and my Tandy 1000EX)

Writing programs for old systems is having an interesting side effect -- at least for me... portability. Emulators are ported to pretty much every modern system, so by writing a game for an old machine, you suddenly make it available to everyone.

code_glitch
14-02-2011, 05:37 PM
There is one interesting aspect of all this: if the super-optimized code designed for a few Mhz and KB could be 'adapted' (even if at a slight performance loss) to work with todays' PCs - imagine the speed you could pull out of it! XD It's like when I had to load DOS 7.1 on a quad core machine: basically DOS on steroids.

Now, that has even more potential ;)

paul_nicholls
14-02-2011, 10:18 PM
Cool to see someone else targeting retro hardware... I just finished a DOS game targeting a 128k 4.77mhz 8088 as the minimum spec... though I've been also working in DEFT Pascal on the TRS-80 Color Computer.]

Neat :) I never had a TRS-80 computer though, only the C64 (and then 128D).


Was there no original pascal compiler for the C64 in the first place? Admittedly, a modern optimizing compiler meant for the target system would be a huge step up (just compare TP7 to TP3) but surely there must have been SOMETHING.

I mean, on the Coco we had three -- OS/9 Pascal (which sucked but is what most people used), DEFT Pascal (the only one that made standalone .bin files you could cloadm or loadm and exec while having access to all 64k of memory if present) and StarPAS (which was just a p-code interpreter)... surely there must have been something for the C=64.]

There were a bunch of Pascal compilers for the C64, but I wanted something native to windows, etc. that was easier to use :)

It is also a fun project + diversion for me too LOL


Don't suppose you are planning on adding VIC=20 support to that? Just thinking I've still got one here. (along with my 64K coco 1 and my Tandy 1000EX)

I hadn't thought of VIC-20 support, but maybe I will...first I will get C64 support going ;)


Writing programs for old systems is having an interesting side effect -- at least for me... portability. Emulators are ported to pretty much every modern system, so by writing a game for an old machine, you suddenly make it available to everyone.

Yeah! Emulators rock! On a nostalgia note, I do wish I had a real C64 though still though - it is what I learnt programming on part way between my 11th and 12 birthday ;)

Oh well...

cheers,
Paul

paul_nicholls
08-06-2011, 09:36 AM
Hey all,
I hadn't worked on pas2c64 since February, so I thought I would have a bit more of a play with it. Since I have been using Lazarus 0.9.30 I thought I would have a go at converting over the program from Delphi to Lazarus using the automatic conversion.

It actually worked perfectly!! Everything still works, even considering I am using SynEdit as the programming GUI part...I was pleasantly surprised!! :D

So now I am continuing this using Lazarus now :)

I am also now working on converting Delphi floating point numbers to c64 floating point numbers using this site as a basis for an algorithm I am writing:

ftp://n2dvm.com/Commodore/Commie-CDs/Kazez%20FREE-CD/c64-knowledge-base/197.htm

I have a good feeling about this, so hopefully soon I will also be able to convert floating point numbers using the parser to c64 floating point format (4-byte mantissa + 1 byte exponent, ie. compressed memory version)...

Once this works I will be able to use the C64 ROM routines to do any maths using floating point numbers! :D

cheers,
Paul

Darthman
08-06-2011, 11:11 AM
Great news! Still waiting release :D

paul_nicholls
08-06-2011, 11:40 AM
Wow! It looks like I might have cracked this 'nut' wide open :D

It seems I can now convert Delphi Doubles to c64 5-byte (packed memory representation, used for variables, etc) and 6-byte (c64 FP register) floating point representations!!

I used the description found below to make the algorithms:
ftp://n2dvm.com/Commodore/Commie-CDs/Kazez%20FREE-CD/c64-knowledge-base/197.htm

Here is the code if anyone is interested :)



PC64MemFloat = ^TC64MemFloat;
TC64MemFloat = packed record
Exponent: Byte;
Mantissa: array[0..3] of Byte;
end;

PC64RegFloat = ^TC64RegFloat;
TC64RegFloat = packed record
Exponent: Byte;
Mantissa: array[0..3] of Byte;
Sign: Byte;
end;

procedure FloatToC64Float(num: Double; out aC64Float: TC64MemFloat); overload;
procedure FloatToC64Float(num: Double; out aC64Float: TC64RegFloat); overload;
function C64FloatToStr(var aC64Float: TC64MemFloat): String; overload;
function C64FloatToStr(var aC64Float: TC64RegFloat): String; overload;

implementation

procedure FloatToC64Float(num: Double; out aC64Float: TC64MemFloat);
// converts a floating point number to 5-byte memory FP representation: exponent (1), mantissa (4)
//ftp://n2dvm.com/Commodore/Commie-CDs/Kazez%20FREE-CD/c64-knowledge-base/197.htm
var
ExpCount: Integer;
SignBit: Integer;
Index: Integer;
begin
Write(Format('%.10f = ',[num]));

// save sign bit
SignBit := 0;
if num < 0 then
begin
SignBit := 128;
num := -num;
end;

if Abs(num) < 0.000001 then
begin
aC64Float.Exponent := 0;
aC64Float.Mantissa[0] := 0;
aC64Float.Mantissa[1] := 0;
aC64Float.Mantissa[2] := 0;
aC64Float.Mantissa[3] := 0;

C64FloatToStr(aC64Float);
Exit;
end;

// calculate exponent byte
ExpCount := 0;
if num < 1 then
while num < 1 do
begin
Dec(ExpCount);
num := num * 2;
end
else
if num >= 2 then
while num >= 2 do
begin
Inc(ExpCount);
num := num / 2;
end;
aC64Float.Exponent := 129 + ExpCount;

num := Frac(num / 2); // 'un-normalize' it for forther processing (immediate mantissa)

// calculate mantissa digits
for Index := 0 to 3 do
begin
num := num * 256;
aC64Float.Mantissa[Index] := Trunc(num);
num := Frac(num);
end;

// round last mantissa digit when required
if num > 0.5 then Inc(aC64Float.Mantissa[3]);

// include sign bit in first mantissa digit
aC64Float.Mantissa[0] := (aC64Float.Mantissa[0] and $7F) or SignBit;

C64FloatToStr(aC64Float);
end;

procedure FloatToC64Float(num: Double; out aC64Float: TC64RegFloat);
// converts a floating point number to 6-byte register FP representation: exponent (1), mantissa (4), separate sign (1)
//ftp://n2dvm.com/Commodore/Commie-CDs/Kazez%20FREE-CD/c64-knowledge-base/197.htm
var
ExpCount: Integer;
SignBit: Integer;
Index: Integer;
begin
Write(Format('%.10f = ',[num]));

// save sign bit
SignBit := 0;
if num < 0 then
begin
SignBit := 128;
num := -num;
end;

if Abs(num) < 0.000001 then
begin
aC64Float.Exponent := 0;
aC64Float.Mantissa[0] := 0;
aC64Float.Mantissa[1] := 0;
aC64Float.Mantissa[2] := 0;
aC64Float.Mantissa[3] := 0;
aC64Float.Sign := 0;

C64FloatToStr(aC64Float);
Exit;
end;

// calculate exponent byte
ExpCount := 0;
if num < 1 then
while num < 1 do
begin
Dec(ExpCount);
num := num * 2;
end
else
if num >= 2 then
while num >= 2 do
begin
Inc(ExpCount);
num := num / 2;
end;
aC64Float.Exponent := 129 + ExpCount;

num := Frac(num / 2); // 'un-normalize' it for forther processing (immediate mantissa)

// calculate mantissa digits
for Index := 0 to 3 do
begin
num := num * 256;
aC64Float.Mantissa[Index] := Trunc(num);
num := Frac(num);
end;

// round last mantissa digit when required
if num > 0.5 then Inc(aC64Float.Mantissa[3]);

// include sign bit in sign part
aC64Float.Mantissa[4] := SignBit;

C64FloatToStr(aC64Float);
end;

function C64FloatToStr(var aC64Float: TC64MemFloat): String;
begin
//output C64 mem floating point as hex (Exponent, Mantissa)
WriteLn(Format('$%.2x $%.2x $%.2x $%.2x $%.2x (mem FP)',
[aC64Float.Exponent,
aC64Float.Mantissa[0],
aC64Float.Mantissa[1],
aC64Float.Mantissa[2],
aC64Float.Mantissa[3]]));
end;

function C64FloatToStr(var aC64Float: TC64RegFloat): String;
begin
//output C64 reg floating point as hex (Exponent, Mantissa, Sign)
WriteLn(Format('$%.2x $%.2x $%.2x $%.2x $%.2x $%.2x (reg FP)',
[aC64Float.Exponent,
aC64Float.Mantissa[0],
aC64Float.Mantissa[1],
aC64Float.Mantissa[2],
aC64Float.Mantissa[3],
aC64Float.Sign]));
end;


I can do this:


var
C64MemFLoat: TC64MemFloat;
C64RegFLoat: TC64RegFloat;
begin
FloatToC64Float(0,C64RegFLoat);
FloatToC64Float(1,C64RegFLoat);
FloatToC64Float(2,C64RegFLoat);
FloatToC64Float(3,C64RegFLoat);
FloatToC64Float(1/2,C64RegFLoat);
FloatToC64Float(1/4,C64RegFLoat);
FloatToC64Float(-67,C64RegFLoat);

FloatToC64Float(13,C64RegFLoat);

FloatToC64Float(0,C64MemFLoat);
FloatToC64Float(3.141592654,C64MemFLoat);
FloatToC64Float(+27,C64MemFLoat);

ReadLn;
end.

and get this output (seems to exactly match what examples I have seen online!):D

0.0000000000 = $00 $00 $00 $00 $00 $00 (reg FP)
1.0000000000 = $81 $80 $00 $00 $00 $00 (reg FP)
2.0000000000 = $82 $80 $00 $00 $00 $00 (reg FP)
3.0000000000 = $82 $C0 $00 $00 $00 $00 (reg FP)
0.5000000000 = $80 $80 $00 $00 $00 $00 (reg FP)
0.2500000000 = $7F $80 $00 $00 $00 $00 (reg FP)
-67.0000000000 = $87 $86 $00 $00 $00 $80 (reg FP)
13.0000000000 = $84 $D0 $00 $00 $00 $00 (reg FP)
0.0000000000 = $00 $00 $00 $00 $00 (mem FP)
3.1415926540 = $82 $49 $0F $DA $A3 (mem FP)
27.0000000000 = $85 $58 $00 $00 $00 (mem FP)


Great news! Still waiting release :D

LOL thanks! if I get much further, I might make an open source project (read-only for now) somewhere so people can follow using SVN, etc. :)

Oh, I am also going to see if I can make the reverse, following the instructions on that site so I can go from c64 FP to Delphi Double for debugging, etc.

cheers,
Paul

paul_nicholls
09-06-2011, 11:11 PM
After testing, and some assembly help from forum.6502.org, I can now create C64 floating point numbers and at least print them out in a C64 emulator!!

This input:


program Test;
begin
WriteLn(102);
end;

Generated this assembler:


.org $0800 ; start at BASIC
.byte $00 $0c $08 $0a $00 $9e $20 $32 ; encode SYS 2064
.byte $30 $36 $34 $00 $00 $00 $00 $00 ; as BASIC line

Lab2064
JMP main
main
;load floating point number at L1 into FAC1
LDA #<L1
LDY #>L1
JSR $BBA2

;convert number in FAC1 to ASCII (pointer in A & Y)
JSR $BDDD

;store pointer of ASCII string into zero-page
STA $FB
STY $FB + 1

;loop through each character in the ASCII and print it out (stop on NULL ($00) value)
LDY #$00
L0
LDA ($FB),Y
BEQ L2
JSR $FFD2
INY
JMP L0
L1
.byte $87 $4C $00 $00 $00
L2
RTS

And this in turn ran on the C64 emulator and printed out the number I entered in the program :)

This means that after some refactoring, I should now be able to add expressions to my compiler and do some math on the C64 :)

cheers,
Paul

Ñuño Martínez
13-06-2011, 07:51 PM
Envy... I have my Pascal compiler project for Z-80 death right now, and I can't work on it now. :(

phibermon
13-06-2011, 08:07 PM
Paul this is all kinds of awesome! Perhaps one day this means I can re-create my first ever computer program on the C64 in pascal!

"What is your name? John
What is your age? 8

Wow, that's amazing John, I'm 8 years old too!"

paul_nicholls
13-06-2011, 08:32 PM
Envy... I have my Pascal compiler project for Z-80 death right now, and I can't work on it now. :(

Why can't you work on it? You just don't feel like it?

cheers,
Paul

paul_nicholls
13-06-2011, 08:32 PM
Paul this is all kinds of awesome! Perhaps one day this means I can re-create my first ever computer program on the C64 in pascal!

"What is your name? John
What is your age? 8

Wow, that's amazing John, I'm 8 years old too!"

haha! Brilliant :)

I am going to be a while away from this point though yet ;)

cheers,
Paul

code_glitch
13-06-2011, 08:38 PM
Hey paul, just dropping in... Any news on progress and what works? just wondering out of interest, it would be quite the cool project. I might just dig up some source from an earlier terminal games I made and port them to one of the many C64 emulators on Ubuntu App Store (ok software centre before someone get offended ;))...

paul_nicholls
13-06-2011, 08:53 PM
Hey mate :)

Well, it barely does anything yet - You can poke memory locations with values, and write out strings and numbers, but that is about it for now LOL

I now have code to create c64 floating point numbers in my parser so this is quite useful...

Hopefully I will get it doing something more very soon :)

cheers,
Paul

code_glitch
13-06-2011, 09:26 PM
write out strings and numbers,


Thats half of what I would need right there ;) How are the if...else and arithmetic operatios coming along? Almost done in my book for its first public compilation.

paul_nicholls
13-06-2011, 11:17 PM
Thats half of what I would need right there ;) How are the if...else and arithmetic operatios coming along? Almost done in my book for its first public compilation.

:) I haven't done if then or other flow control constructs yet or arithmetic operations yet, but I hope to get those in "soon" - I need to figure out how I am going to do boolean with the 6502 for starters :D

cheers,
Paul

paul_nicholls
14-06-2011, 11:06 PM
Nice! I have now gotten the parsing of expressions added in :D

It spits out an expression list (operand operand operator ....) so I can check the output..."all I have to do now" (tm) is make it output some code for expressions haha

cheers,
Paul

paul_nicholls
15-06-2011, 04:43 AM
Ok, for the time being, I am going to support these types in my compiler - integer (signed 16-bit), single (32-bit), and string (up to 255 characters max)...

I can now read in Pascal syntax constants and variables using these standard types, and store them in generated assembly code.

I feel I am very close to actually doing some assignment statements, and probably some math too in the compiler :)

I have also found out how to detect key presses, so I will have routines for this too; WaitForAnyKey and WaitForKey(some key code), and probably even IsKeyPressed(some key code).

I think I know how I will do inputting of strings from the keyboard (not sure about numbers though yet)...

cheers,
Paul

paul_nicholls
15-06-2011, 12:32 PM
After I found a very simple c64 interrupt routine here:

http://xabreman.wordpress.com/2011/01/02/6510-asm-simple-interrupt-example/

I thought that I can do this too, so by adding some new routines into the Pascal language & their support framework (parsing + code generation), I can now actually do this very same interrupt routine using compiled Pascal routines:


program Test;

procedure irqtest; interrupt;
begin
IncMemB($d020);
StdIRQ;
end;

procedure Init;
begin
SetInterrupt(irqtest);
end;

begin
Init;
end.

which produces this:

:BasicUpstart2(main) // 10 sys <start address>

.import source "rtl\Macros_RTL.asm"
.import source "rtl\Consts_RTL.asm"

int_irqtest:
inc $d020
jmp STDIRQ
rti
proc_init:
sei // disable interrupts

// set a custom interrupt routine address to interrupt vector

lda #<int_irqtest // low byte of irqtest start addr
ldx #>int_irqtest // high byte of irqtest start addr
sta IRQVECLO
stx IRQVECHI

cli // clear interrupt disable bit
rts
main:
jsr proc_init
rts

and when run in the c64 VICE emulator, it hooks the interrupt routine into the interrupt vector and merrily runs in the background incrementing the background colour! :D

Now this is progress!! haha

cheers,
Paul

Ñuño Martínez
15-06-2011, 01:09 PM
Why can't you work on it? You just don't feel like it?

cheers,
PaulI have a lot of open projects, and I don't know how to say "no" (so when somebody propose something I always say "yes" :(), and I'm not in my best moment right now...

paul_nicholls
16-06-2011, 01:03 AM
Well, I have come up with a logo for my pas2c64 project:
http://img34.imageshack.us/img34/4144/logo3ec.png (http://imageshack.us/photo/my-images/34/logo3ec.png/)

What do you guys think? I quite like it, but feel free to comment, I value your feedback :)

cheers,
Paul

mobilus
16-06-2011, 04:14 AM
I'd prefer Pas2Atari800XE, but your logo looks good too ;)

paul_nicholls
16-06-2011, 04:18 AM
I'd prefer Pas2Atari800XE, but your logo looks good too ;)

haha! thanks mate :)

cheers,
Paul

paul_nicholls
21-06-2011, 07:10 AM
Neat! I have added a handy feature to pas2c64 - allowing pure 6502 assembly code procedure that I can call from the rest of the program!

This is great for testing and extending pas2c64 without even adding new features and recompiling :)

Here is an example:

procedure UpperCase; assembler;
asm
lda $d018
and #253
sta $d018
end;

procedure LowerCase; assembler;
asm
lda $d018
ora #2
sta $d018
end;

begin
LowerCase;
UpperCase;
end.

cheers,
Paul