sixty-five-oh-two: An eDSL for writing 65(C)02 bytecode.

[ dsl, library, mit ] [ Propose Tags ]

An eDSL for writing 65(C)02 bytecode. Please see the README on GitHub at

[Skip to Readme]
Versions [RSS] [faq],,
Change log
Dependencies base (>=4.7 && <5), bytestring, containers, lens, mtl [details]
License MIT
Copyright 2018 Tyler Limkemann
Author Tyler Limkemann
Category DSL
Home page
Bug tracker
Source repo head: git clone
Uploaded by aearnus at 2018-05-26T20:26:55Z
Distributions NixOS:
Downloads 1342 total (14 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Hackage Matrix CI
Docs available [build log]
Last success reported on 2018-05-26 [all 1 reports]




Maintainer's Corner

For package maintainers and hackage trustees


Readme for sixty-five-oh-two-

[back to package description]

DSL.SixtyFiveOhTwo: A 65C02 Assembly eDSL in Haskell

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.