A 65C02 Assembly eDSL in Haskell
You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Aearnus ddda2c6ae9 Version bump 5 years ago
src/DSL Made functions complete, no more partial functions 5 years ago
test Removed the executable from the library, moved it to the test (Version bump) 5 years ago
.gitignore Updated new name 5 years ago
ChangeLog.md Version bump 5 years ago
LICENSE MIT 5 years ago
README.md Fixed the readme star count 5 years ago
Setup.hs init 5 years ago
fancy_banner.png fun stuff 5 years ago
package.yaml Version bump 5 years ago
screenshot.png fun stuff 5 years ago
stack.yaml init 5 years ago


DSL.SixtyFiveOhTwo: A 65C02 Assembly eDSL in Haskell

Hackage 100% 65C02 Coverage GitHub stars

Example image

... shut up, show me the code!

Here's some example code utilizing all of the features of the eDSL:

import DSL.SixtyFiveOhTwo

accumulatorLoadNStore :: Instruction
accumulatorLoadNStore = do
    lda (Immediate 0x10)
    sta (Absolute 0x0200)
    rts (Implied)

myProgram :: Instruction
myProgram = do
    define "accumulatorLoadNStore" accumulatorLoadNStore
    call "accumulatorLoadNStore"

Here's a fun little snippet that adds 10 to the accumulator using Haskell Monad Magic™️:

test3f2 :: Instruction
test3f2 = replicateM_ 10 (inc (Accumulator))

Everything that this module exposes is in src/DSL/SixtyFiveOhTwo.hs. A quick browse through this file will reveal the full extent of the features of this eDSL.

What is this?

This is an embedded Domain Specific Language that allows a user to write code that runs on the 65C02 CPU. This is the CPU that runs devices such as the Apple II, Commodore 64, or the NES.

What does the language provide me?

  • Full coverage. Everything bit of code that the 65C02 can understand is represented in this language. Everywhere adc to wai can be used. These opcodes are represented as generic operations, each of which simply append to the bytecode that gets passed into it. Here's an example of the definition for a certain opcode:
lda :: AddressingMode -> Instruction
lda (Immediate b) = genericOp 169 b
lda (ZeroPage b) = genericOp 165 b
lda (ZeroPageX b) = genericOp 181 b
lda (Absolute b) = genericTwoByteOp 173 b
lda (AbsoluteX b) = genericTwoByteOp 189 b
lda (AbsoluteY b) = genericTwoByteOp 185 b
lda (ZeroPageIndirect b) = genericOp 178 b
lda (IndirectX b) = genericOp 161 b
lda (IndirectY b) = genericOp 177 b
  • Type safety. Every addressing mode is represented the Haskell type system, and thus issues will be caught at compile time. The AddressingMode ADT is used to represent a function's addressing mode, and opcodes do not take addressing modes that they do not support.
data AddressingMode =
    Implied |
    Accumulator |
    Immediate Word8 |
    Relative Int8 | -- Signed
    ZeroPageRelative Int8 | -- Signed
    Absolute Word16 |
    AbsoluteX Word16 |
    AbsoluteY Word16 |
    ZeroPage Word8 |
    ZeroPageX Word8 |
    ZeroPageY Word8 |
    ZeroPageIndirect Word8 |
    Indirect Word16 |
    IndirectX Word8 |
    IndirectY Word8
  • Easy abstractions. The define and call keywords automatically generate the code necessary to create and call subroutines.

Support or Donate

Please contact me if you have any wish to support this project or any other projects I've worked on. The information is in package.yaml.