Implementing Lisp for 6502/65C816
Posted: Mon Jul 20, 2009 1:17 am
Hi,
Edit: I forgot to mention that if you know any existing Lisp implementation for 6502 I am also interested to have a look at it for ideas, although I'm quite convinced I need to write my own to get what I really want.
I used to program C64 in assembly back in the '90s, writing a few intros and utils, and played C64 games several hours a day as a child.
I'd like to implement a simple Lisp, something similar to Scheme dialect, for my C64 and (maybe later) to my non-existing custom-built 65c816 board. C64 is the only 65xx hardware I have ATM so the first version will be for that anyway. As you might know, Lisp is a dynamically typed language, which poses some problems for implementing it efficiently and I could use some optimization ideas. I try to explain my design thoughts so that you will understand them even if you have not implemented Lisp yourself (if you have no idea what it is try http://schemers.org/ ).
I have thought of having a 24-bit general datatype, where first 16 bits represent either an integer (or true/false/end-of-list etc. values) or a pointer. The last byte is a type tag that tells how to interpret the first 16 bits (00=16-bit integer,01=cons cell, 02=pointer to vector, 03=truth value, 04=end-of-list, 05=pointer to rational number, 06=pointer to string.. ..) I would, in addition to this dynamic typing scheme, have functions for 8- and 16-bit operations as an optimization:
The numeric tower would include arbitrarily sized rationals in addition to 16-bit integers, but not floating point numbers as rationals can be used instead, as many people in this forum have pointed out (thank you!).
Garbage collection would be done by scanning ZP and stack (and global variables located elsewhere) for Lisp values, marking pointers in heap and then deallocating any values in heap that are not referenced by anything. Note that the gc does not have to know whether some address in f.e. stack is really a Lisp word or a subroutine return address, because in the worst case some memory that could be freed is retained and I can live with that, since it will happen unfrequently in practice.
The system would of course have both an interpreter (quite easy) and an optimizing compiler (much harder but should be doable). The interpreter is always included in the runtime system, because Lisp code can be created and executed by other Lisp procedures and interpreter is very useful for debugging anyway, when you have debugged some piece of code you just compile it so it will run faster.
Natural choice for an implementation language would be to write this system in Lisp. However 64k of memory (and lack of compiler in the first version) will make this rather difficult, so bulk of the code will be written in 6502 assembler and some parts of the runtime system will maybe be written in Lisp. With 65c816 situation would be different, but as I am more familiar with software than hardware design I will first use existing hardware.
Some words about the compiler: I hope I'm able to fit in in the system, so that I could compile functions one at a time, when I want to do so, without resorting to a PC. I have a general idea: first the compiler does some high-level optimizations on the source code-resembling intermediate representation, then it will lower the code for a Forth-like stack machine, do some peephole optimizations and then (at last
) compile it to machine code. With so few a registers, stack machine will be probably be the best intermediate representation, in my opinion anyway. Any thoughts on this?
Edit: I forgot to mention that if you know any existing Lisp implementation for 6502 I am also interested to have a look at it for ideas, although I'm quite convinced I need to write my own to get what I really want.
I used to program C64 in assembly back in the '90s, writing a few intros and utils, and played C64 games several hours a day as a child.
I'd like to implement a simple Lisp, something similar to Scheme dialect, for my C64 and (maybe later) to my non-existing custom-built 65c816 board. C64 is the only 65xx hardware I have ATM so the first version will be for that anyway. As you might know, Lisp is a dynamically typed language, which poses some problems for implementing it efficiently and I could use some optimization ideas. I try to explain my design thoughts so that you will understand them even if you have not implemented Lisp yourself (if you have no idea what it is try http://schemers.org/ ).
I have thought of having a 24-bit general datatype, where first 16 bits represent either an integer (or true/false/end-of-list etc. values) or a pointer. The last byte is a type tag that tells how to interpret the first 16 bits (00=16-bit integer,01=cons cell, 02=pointer to vector, 03=truth value, 04=end-of-list, 05=pointer to rational number, 06=pointer to string.. ..) I would, in addition to this dynamic typing scheme, have functions for 8- and 16-bit operations as an optimization:
Code: Select all
(+ 10182834 1728373298292992) ; this addition function supports numbers of any size
(int8+ 5 9) ; this adds 8-bit only
(int16+ 1024 363) ; this adds 16-bit only
Garbage collection would be done by scanning ZP and stack (and global variables located elsewhere) for Lisp values, marking pointers in heap and then deallocating any values in heap that are not referenced by anything. Note that the gc does not have to know whether some address in f.e. stack is really a Lisp word or a subroutine return address, because in the worst case some memory that could be freed is retained and I can live with that, since it will happen unfrequently in practice.
The system would of course have both an interpreter (quite easy) and an optimizing compiler (much harder but should be doable). The interpreter is always included in the runtime system, because Lisp code can be created and executed by other Lisp procedures and interpreter is very useful for debugging anyway, when you have debugged some piece of code you just compile it so it will run faster.
Natural choice for an implementation language would be to write this system in Lisp. However 64k of memory (and lack of compiler in the first version) will make this rather difficult, so bulk of the code will be written in 6502 assembler and some parts of the runtime system will maybe be written in Lisp. With 65c816 situation would be different, but as I am more familiar with software than hardware design I will first use existing hardware.
Some words about the compiler: I hope I'm able to fit in in the system, so that I could compile functions one at a time, when I want to do so, without resorting to a PC. I have a general idea: first the compiler does some high-level optimizations on the source code-resembling intermediate representation, then it will lower the code for a Forth-like stack machine, do some peephole optimizations and then (at last