80 lines
3.2 KiB
Markdown
80 lines
3.2 KiB
Markdown
# DSL.SixtyFiveOhTwo: A 65C02 Assembly eDSL in Haskell
|
|
[![Hackage](https://img.shields.io/hackage/v/sixty-five-oh-two.svg)](https://hackage.haskell.org/package/sixty-five-oh-two) ![100% 65C02 Coverage](https://img.shields.io/badge/65C02%20coverage-100%25-brightgreen.svg) ![GitHub stars](https://img.shields.io/github/stars/Aearnus/sixty-five-oh-two.svg?style=social&label=Stars)
|
|
|
|
![Example image](https://raw.githubusercontent.com/Aearnus/sixty-five-oh-two/master/fancy_banner.png)
|
|
|
|
_... shut up, show me the code!_
|
|
|
|
Here's some example code utilizing all of the features of the eDSL:
|
|
|
|
```haskell
|
|
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:tm::
|
|
|
|
```haskell
|
|
test3f2 :: Instruction
|
|
test3f2 = replicateM_ 10 (inc (Accumulator))
|
|
```
|
|
|
|
Everything that this module exposes is in [src/DSL/SixtyFiveOhTwo.hs](https://github.com/Aearnus/sixty-five-oh-two/blob/master/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 **e**mbedded **D**omain **S**pecific **L**anguage 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:
|
|
```haskell
|
|
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.
|
|
```haskell
|
|
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`.
|