aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAstatin <[email protected]>2024-09-19 18:55:25 +0900
committerAstatin <astatin@redacted>2024-09-19 18:55:25 +0900
commit1680bc5146b25d3631e6b7bfee3946ef59d906fd (patch)
tree5aac450b147ab4c02b51f2968caefbc343a0a577
parent7f41367ce01fbecaec6eb5f173152a59fe55e669 (diff)
Create 1st draft of new assembler in go
-rw-r--r--examples/\27
-rw-r--r--examples/minimal.gbasm37
-rw-r--r--examples/minimal.rombin0 -> 363 bytes
-rw-r--r--examples/zoom.gbasm125
-rw-r--r--examples/zoom.rombin0 -> 768 bytes
-rw-r--r--examples/zoom.rom.savbin0 -> 32768 bytes
-rw-r--r--go.mod3
-rw-r--r--instructions.go432
-rw-r--r--main.go55
-rw-r--r--parameters.go134
-rw-r--r--test.gbasm2
11 files changed, 815 insertions, 0 deletions
diff --git a/examples/\ b/examples/\
new file mode 100644
index 0000000..d6b549e
--- /dev/null
+++ b/examples/\
@@ -0,0 +1,27 @@
+.PADTO 0x0100
+Entry:
+ JP =Start
+
+.PADTO 0x0104
+Nintendo_Logo: ; The Nintendo logo must be stored in bytes 0x104-133
+ .DB $CE,$ED,$66,$66,$CC,$0D,$00,$0B,$03,$73,$00,$83,$00,$0C,$00,$0D
+ .DB $00,$08,$11,$1F,$88,$89,$00,$0E,$DC,$CC,$6E,$E6,$DD,$DD,$D9,$99
+ .DB $BB,$BB,$67,$63,$6E,$0E,$EC,$CC,$DD,$DC,$99,$9F,$BB,$B9,$33,$3E
+
+.PADTO 0x0134
+Checksum: ; The bytes 0x134-0x14d need to add up to 0xe7 (= 0xff - 0x19)
+ .DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
+ .DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$e7
+
+Start:
+ LD HL, $8000
+ VRAM_Clearing_Loop:
+ LD A, $00
+ LD (HL+), A
+ LD A, H
+ CP $A0
+ JR NZ, =VRAM_Clearing_Loop
+
+ Lock:
+ JR =Lock
+
diff --git a/examples/minimal.gbasm b/examples/minimal.gbasm
new file mode 100644
index 0000000..33405c3
--- /dev/null
+++ b/examples/minimal.gbasm
@@ -0,0 +1,37 @@
+.PADTO 0x0100
+Entry:
+ JP =Start
+
+.PADTO 0x0104
+Nintendo_Logo: ; The Nintendo logo must be stored in bytes 0x104-133
+ .DB $CE,$ED,$66,$66,$CC,$0D,$00,$0B,$03,$73,$00,$83,$00,$0C,$00,$0D
+ .DB $00,$08,$11,$1F,$88,$89,$00,$0E,$DC,$CC,$6E,$E6,$DD,$DD,$D9,$99
+ .DB $BB,$BB,$67,$63,$6E,$0E,$EC,$CC,$DD,$DC,$99,$9F,$BB,$B9,$33,$3E
+
+.PADTO 0x0134
+Checksum: ; The bytes 0x134-0x14d need to add up to 0xe7 (= 0xff - 0x19)
+ .DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
+ .DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$e7
+
+Start:
+ Wait_VBlank:
+ LD A, (0xFF00+$41)
+ AND $03
+ CP $01
+ JR NZ, =Wait_VBlank
+ LD A, $00
+ LD ($40), A
+
+ LD HL, $8000
+ VRAM_Clearing_Loop:
+ LD A, $ff
+ LD (HL+), A
+ LD A, H
+ CP $A0
+ JR NZ, =VRAM_Clearing_Loop
+
+ LD A, $91
+ LD ($40), A
+
+ Lock:
+ JR =Lock
diff --git a/examples/minimal.rom b/examples/minimal.rom
new file mode 100644
index 0000000..271c355
--- /dev/null
+++ b/examples/minimal.rom
Binary files differ
diff --git a/examples/zoom.gbasm b/examples/zoom.gbasm
new file mode 100644
index 0000000..6ed92a1
--- /dev/null
+++ b/examples/zoom.gbasm
@@ -0,0 +1,125 @@
+.PADTO 0x0040
+Interrupt_VBlank:
+ JP =VBlank
+
+.PADTO 0x0048
+Interrupt_Stat:
+ JP =Effect
+
+.PADTO 0x0100
+ JP =Start
+
+.PADTO 0x0104
+Nintendo_Logo: ; The Nintendo logo must be stored in bytes 0x104-133
+ .DB $CE,$ED,$66,$66,$CC,$0D,$00,$0B,$03,$73,$00,$83,$00,$0C,$00,$0D
+ .DB $00,$08,$11,$1F,$88,$89,$00,$0E,$DC,$CC,$6E,$E6,$DD,$DD,$D9,$99
+ .DB $BB,$BB,$67,$63,$6E,$0E,$EC,$CC,$DD,$DC,$99,$9F,$BB,$B9,$33,$3E
+
+.PADTO 0x0134
+Checksum: ; The bytes 0x134-0x14d need to add up to 0xe7 (= 0xff - 0x19)
+ .DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
+ .DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$e7
+
+.DEFINE Wave_Top $c0
+Start:
+ LD C, $01
+ LD A, $03
+ LD ($FF), A
+ LD A, $40
+ LD ($41), A
+ LD A, $01
+ LD ($44), A
+ LD B, $00
+ LD C, $30
+ LD H, $Wave_Top
+ LD L, $00
+ LD A, $00
+ Clean_Wave.loop:
+ LD (HL+), A
+ CP L
+ JR NZ, =Clean_Wave.loop
+ EI
+Halt_loop:
+ HALT
+ JP Halt_loop
+
+Effect:
+ LD A, ($44)
+ INC A
+ AND $7f
+ LD ($45), A
+ LD E, A
+ LD D, $Wave_Top
+ LD A, (DE)
+ LD D, $02
+ LD E, A
+ LD A, (DE)
+
+ AND $c0
+ RRCA
+ RRCA
+ RRCA
+ RRCA
+ RRCA
+ RRCA
+ SUB $02
+
+ LD ($43), A
+ LD ($42), A
+ RETI
+
+VBlank:
+ INC B
+ LD A, B
+ AND $03
+ JR NZ, =VBlank.end
+ CP C
+ JR Z, =Wave_Draw
+ INC C
+ Wave_Draw:
+ LD H, $Wave_Top
+ LD L, $00
+ VBlank.loop:
+ LD A, (HL)
+ ADD $08
+ LD (HL+), A
+ LD A, C
+ CP L
+ JR NZ, =VBlank.loop
+ VBlank.end:
+ RETI
+
+.PADTO 0x0200
+Sin_wave:
+ .DB 0x80, 0x83, 0x86, 0x89, 0x8C, 0x90, 0x93, 0x96
+ .DB 0x99, 0x9C, 0x9F, 0xA2, 0xA5, 0xA8, 0xAB, 0xAE
+ .DB 0xB1, 0xB3, 0xB6, 0xB9, 0xBC, 0xBF, 0xC1, 0xC4
+ .DB 0xC7, 0xC9, 0xCC, 0xCE, 0xD1, 0xD3, 0xD5, 0xD8
+ .DB 0xDA, 0xDC, 0xDE, 0xE0, 0xE2, 0xE4, 0xE6, 0xE8
+ .DB 0xEA, 0xEB, 0xED, 0xEF, 0xF0, 0xF1, 0xF3, 0xF4
+ .DB 0xF5, 0xF6, 0xF8, 0xF9, 0xFA, 0xFA, 0xFB, 0xFC
+ .DB 0xFD, 0xFD, 0xFE, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF
+ .DB 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFE, 0xFD
+ .DB 0xFD, 0xFC, 0xFB, 0xFA, 0xFA, 0xF9, 0xF8, 0xF6
+ .DB 0xF5, 0xF4, 0xF3, 0xF1, 0xF0, 0xEF, 0xED, 0xEB
+ .DB 0xEA, 0xE8, 0xE6, 0xE4, 0xE2, 0xE0, 0xDE, 0xDC
+ .DB 0xDA, 0xD8, 0xD5, 0xD3, 0xD1, 0xCE, 0xCC, 0xC9
+ .DB 0xC7, 0xC4, 0xC1, 0xBF, 0xBC, 0xB9, 0xB6, 0xB3
+ .DB 0xB1, 0xAE, 0xAB, 0xA8, 0xA5, 0xA2, 0x9F, 0x9C
+ .DB 0x99, 0x96, 0x93, 0x90, 0x8C, 0x89, 0x86, 0x83
+ .DB 0x80, 0x7D, 0x7A, 0x77, 0x74, 0x70, 0x6D, 0x6A
+ .DB 0x67, 0x64, 0x61, 0x5E, 0x5B, 0x58, 0x55, 0x52
+ .DB 0x4F, 0x4D, 0x4A, 0x47, 0x44, 0x41, 0x3F, 0x3C
+ .DB 0x39, 0x37, 0x34, 0x32, 0x2F, 0x2D, 0x2B, 0x28
+ .DB 0x26, 0x24, 0x22, 0x20, 0x1E, 0x1C, 0x1A, 0x18
+ .DB 0x16, 0x15, 0x13, 0x11, 0x10, 0x0F, 0x0D, 0x0C
+ .DB 0x0B, 0x0A, 0x08, 0x07, 0x06, 0x06, 0x05, 0x04
+ .DB 0x03, 0x03, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01
+ .DB 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03
+ .DB 0x03, 0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x0A
+ .DB 0x0B, 0x0C, 0x0D, 0x0F, 0x10, 0x11, 0x13, 0x15
+ .DB 0x16, 0x18, 0x1A, 0x1C, 0x1E, 0x20, 0x22, 0x24
+ .DB 0x26, 0x28, 0x2B, 0x2D, 0x2F, 0x32, 0x34, 0x37
+ .DB 0x39, 0x3C, 0x3F, 0x41, 0x44, 0x47, 0x4A, 0x4D
+ .DB 0x4F, 0x52, 0x55, 0x58, 0x5B, 0x5E, 0x61, 0x64
+ .DB 0x67, 0x6A, 0x6D, 0x70, 0x74, 0x77, 0x7A, 0x7D
diff --git a/examples/zoom.rom b/examples/zoom.rom
new file mode 100644
index 0000000..f6b1239
--- /dev/null
+++ b/examples/zoom.rom
Binary files differ
diff --git a/examples/zoom.rom.sav b/examples/zoom.rom.sav
new file mode 100644
index 0000000..12f3be4
--- /dev/null
+++ b/examples/zoom.rom.sav
Binary files differ
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..b12abb4
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,3 @@
+module github.com/astatinchan/gamboy-asm
+
+go 1.23.0
diff --git a/instructions.go b/instructions.go
new file mode 100644
index 0000000..e78dbab
--- /dev/null
+++ b/instructions.go
@@ -0,0 +1,432 @@
+package main
+
+import (
+ "fmt"
+ "strings"
+)
+
+type ParamType func(param string) (uint16, error)
+
+type InstructionParams struct {
+ Types []ParamType
+ Assembler func(args []uint16) []uint8
+}
+
+type InstructionSet map[string][]InstructionParams
+
+var Instructions = InstructionSetNew()
+
+func InstructionSetNew() InstructionSet {
+ result := make(InstructionSet)
+
+ result["LD"] = []InstructionParams{
+ {
+ Types: []ParamType{Reg8, Reg8},
+ Assembler: func(args []uint16) []byte {
+ return []byte{0b01000000 | (uint8(uint8(args[0])) << 3) | uint8(uint8(args[1]))}
+ },
+ },
+ {
+ Types: []ParamType{Reg8, Raw8},
+ Assembler: func(args []uint16) []byte { return []byte{0b00000110 | (uint8(args[0]) << 3), uint8(args[1])} },
+ },
+ {
+ Types: []ParamType{A, Reg16Indirect},
+ Assembler: func(args []uint16) []byte { return []byte{0b00001010 | uint8(args[1])<<4} },
+ },
+ {
+ Types: []ParamType{Reg16Indirect, A},
+ Assembler: func(args []uint16) []byte { return []byte{0b00000010 | uint8(args[1])<<4} },
+ },
+ {
+ Types: []ParamType{A, Raw16Indirect},
+ Assembler: func(args []uint16) []byte { return []byte{0b11111010, uint8(args[1]) & 0xff, uint8(args[1] >> 8)} },
+ },
+ {
+ Types: []ParamType{Raw16Indirect, A},
+ Assembler: func(args []uint16) []byte { return []byte{0b11101010, uint8(args[0]) & 0xff, uint8(args[0] >> 8)} },
+ },
+ {
+ Types: []ParamType{A, IndirectC},
+ Assembler: func(args []uint16) []byte { return []byte{0b11110010} },
+ },
+ {
+ Types: []ParamType{IndirectC, A},
+ Assembler: func(args []uint16) []byte { return []byte{0b11100010} },
+ },
+ {
+ Types: []ParamType{A, Raw8Indirect},
+ Assembler: func(args []uint16) []byte { return []byte{0b11110000, uint8(args[1])} },
+ },
+ {
+ Types: []ParamType{Raw8Indirect, A},
+ Assembler: func(args []uint16) []byte { return []byte{0b11100000, uint8(args[0])} },
+ },
+ {
+ Types: []ParamType{Reg16, Raw16},
+ Assembler: func(args []uint16) []byte {
+ return []byte{
+ 0b00000001 | (uint8(args[0]) << 4),
+ uint8(args[1]) & 0xff,
+ uint8(args[1] >> 8),
+ }
+ },
+ },
+ {
+ Types: []ParamType{Raw16Indirect, SP},
+ Assembler: func(args []uint16) []byte { return []byte{0b00001000, uint8(args[0]) & 0xff, uint8(args[0] >> 8)} },
+ },
+ {
+ Types: []ParamType{SP, HL},
+ Assembler: func(args []uint16) []byte { return []byte{0b11111001} },
+ },
+ {
+ Types: []ParamType{HL, Raw8},
+ Assembler: func(args []uint16) []byte { return []byte{0b11111000, uint8(args[1])} },
+ },
+ }
+ result["PUSH"] = []InstructionParams{
+ {
+ Types: []ParamType{Reg16},
+ Assembler: func(args []uint16) []byte { return []byte{0b11000101 | (uint8(args[0]) << 4)} },
+ },
+ }
+ result["POP"] = []InstructionParams{
+ {
+ Types: []ParamType{Reg16},
+ Assembler: func(args []uint16) []byte { return []byte{0b11000001 | (uint8(args[0]) << 4)} },
+ },
+ }
+ result["ADD"] = []InstructionParams{
+ {
+ Types: []ParamType{Reg8},
+ Assembler: func(args []uint16) []byte { return []byte{0b10000000 | (uint8(args[0]))} },
+ },
+ {
+ Types: []ParamType{Raw8},
+ Assembler: func(args []uint16) []byte { return []byte{0b11000110, uint8(args[0])} },
+ },
+ {
+ Types: []ParamType{SP, Raw8},
+ Assembler: func(args []uint16) []byte { return []byte{0b11101000, uint8(args[1])} },
+ },
+ }
+ result["ADC"] = []InstructionParams{
+ {
+ Types: []ParamType{Reg8},
+ Assembler: func(args []uint16) []byte { return []byte{0b10001000 | uint8(args[0])} },
+ },
+ {
+ Types: []ParamType{Raw8},
+ Assembler: func(args []uint16) []byte { return []byte{0b11001110, uint8(args[0])} },
+ },
+ }
+ result["SUB"] = []InstructionParams{
+ {
+ Types: []ParamType{Reg8},
+ Assembler: func(args []uint16) []byte { return []byte{0b10010000 | (uint8(args[0]))} },
+ },
+ {
+ Types: []ParamType{Raw8},
+ Assembler: func(args []uint16) []byte { return []byte{0b11010110, uint8(args[0])} },
+ },
+ }
+ result["SBC"] = []InstructionParams{
+ {
+ Types: []ParamType{Reg8},
+ Assembler: func(args []uint16) []byte { return []byte{0b10011000 | uint8(args[0])} },
+ },
+ {
+ Types: []ParamType{Raw8},
+ Assembler: func(args []uint16) []byte { return []byte{0b11011110, uint8(args[0])} },
+ },
+ }
+ result["CP"] = []InstructionParams{
+ {
+ Types: []ParamType{Reg8},
+ Assembler: func(args []uint16) []byte { return []byte{0b10111000 | (uint8(args[0]))} },
+ },
+ {
+ Types: []ParamType{Raw8},
+ Assembler: func(args []uint16) []byte { return []byte{0b11111110, uint8(args[0])} },
+ },
+ }
+ result["INC"] = []InstructionParams{
+ {
+ Types: []ParamType{Reg8},
+ Assembler: func(args []uint16) []byte { return []byte{0b00000100 | (uint8(args[0]) << 3)} },
+ },
+
+ {
+ Types: []ParamType{Reg16},
+ Assembler: func(args []uint16) []byte { return []byte{0b00000011 | (uint8(args[0]) << 4)} },
+ },
+ }
+ result["DEC"] = []InstructionParams{
+ {
+ Types: []ParamType{Reg8},
+ Assembler: func(args []uint16) []byte { return []byte{0b00000101 | (uint8(args[0]) << 3)} },
+ },
+
+ {
+ Types: []ParamType{Reg16},
+ Assembler: func(args []uint16) []byte { return []byte{0b00001011 | (uint8(args[0]) << 4)} },
+ },
+ }
+ result["AND"] = []InstructionParams{
+ {
+ Types: []ParamType{Reg8},
+ Assembler: func(args []uint16) []byte { return []byte{0b10100000 | (uint8(args[0]))} },
+ },
+ {
+ Types: []ParamType{Raw8},
+ Assembler: func(args []uint16) []byte { return []byte{0b11100110, uint8(args[0])} },
+ },
+ }
+ result["OR"] = []InstructionParams{
+ {
+ Types: []ParamType{Reg8},
+ Assembler: func(args []uint16) []byte { return []byte{0b10110000 | (uint8(args[0]))} },
+ },
+ {
+ Types: []ParamType{Raw8},
+ Assembler: func(args []uint16) []byte { return []byte{0b11110110, uint8(args[0])} },
+ },
+ }
+ result["XOR"] = []InstructionParams{
+ {
+ Types: []ParamType{Reg8},
+ Assembler: func(args []uint16) []byte { return []byte{0b10101000 | (uint8(args[0]))} },
+ },
+ {
+ Types: []ParamType{Raw8},
+ Assembler: func(args []uint16) []byte { return []byte{0b11101110, uint8(args[0])} },
+ },
+ }
+ result["CCF"] = []InstructionParams{
+ {Types: []ParamType{}, Assembler: func(args []uint16) []byte { return []byte{0b00111111} }},
+ }
+ result["SCF"] = []InstructionParams{
+ {Types: []ParamType{}, Assembler: func(args []uint16) []byte { return []byte{0b00110111} }},
+ }
+ result["DAA"] = []InstructionParams{
+ {Types: []ParamType{}, Assembler: func(args []uint16) []byte { return []byte{0b00100111} }},
+ }
+ result["CPL"] = []InstructionParams{
+ {Types: []ParamType{}, Assembler: func(args []uint16) []byte { return []byte{0b00101111} }},
+ }
+ result["JP"] = []InstructionParams{
+ {
+ Types: []ParamType{Raw16},
+ Assembler: func(args []uint16) []byte { return []byte{0b11000011, uint8(args[0]) & 0xff, uint8(args[0] >> 8)} },
+ },
+ {
+ Types: []ParamType{HL},
+ Assembler: func(args []uint16) []byte { return []byte{0b11101001} },
+ },
+ {
+ Types: []ParamType{Condition, Raw16},
+ Assembler: func(args []uint16) []byte {
+ return []byte{
+ 0b11000010 | (uint8(args[0]) << 3),
+ uint8(args[1]) & 0xff,
+ uint8(args[1] >> 8),
+ }
+ },
+ },
+ }
+ result["JR"] = []InstructionParams{
+ {
+ Types: []ParamType{Raw8},
+ Assembler: func(args []uint16) []byte { return []byte{0b00011000, uint8(args[0])} },
+ },
+ // TODO: Add the relative thingies somehow
+ {
+ Types: []ParamType{Raw16},
+ Assembler: func(args []uint16) []byte { return []byte{0b00011000, uint8(args[0])} },
+ },
+ {
+ Types: []ParamType{Condition, Raw8},
+ Assembler: func(args []uint16) []byte { return []byte{0b00100000 | (uint8(args[0]) << 3), uint8(args[1])} },
+ },
+ // {
+ // Types: []ParamType{Condition, Raw16},
+ // Assembler: func(args []uint16) []byte { return []byte{0b00100000 | (uint8(args[0]) << 3), uint8(args[1])} },
+ // },
+ }
+ result["CALL"] = []InstructionParams{
+ {
+ Types: []ParamType{Raw16},
+ Assembler: func(args []uint16) []byte { return []byte{0b11001101, uint8(args[0]) & 0xff, uint8(args[0] >> 8)} },
+ },
+ {
+ Types: []ParamType{Condition, Raw16},
+ Assembler: func(args []uint16) []byte {
+ return []byte{
+ 0b11000100 | (uint8(args[0]) << 3),
+ uint8(args[1]) & 0xff,
+ uint8(args[1] >> 8),
+ }
+ },
+ },
+ }
+ result["RET"] = []InstructionParams{
+ {Types: []ParamType{}, Assembler: func(args []uint16) []byte { return []byte{0b11001001} }},
+ {
+ Types: []ParamType{Condition},
+ Assembler: func(args []uint16) []byte { return []byte{0b11000000 | (uint8(args[0]) << 3)} },
+ },
+ }
+ result["RETI"] = []InstructionParams{
+ {Types: []ParamType{}, Assembler: func(args []uint16) []byte { return []byte{0b11011001} }},
+ }
+ result["RST"] = []InstructionParams{
+ {
+ Types: []ParamType{BitOrdinal},
+ Assembler: func(args []uint16) []byte { return []byte{0b11000111 | (uint8(args[0]) << 3)} },
+ },
+ }
+ result["DI"] = []InstructionParams{
+ {Types: []ParamType{}, Assembler: func(args []uint16) []byte { return []byte{0b11110011} }},
+ }
+ result["EI"] = []InstructionParams{
+ {Types: []ParamType{}, Assembler: func(args []uint16) []byte { return []byte{0b11111011} }},
+ }
+ result["NOP"] = []InstructionParams{
+ {Types: []ParamType{}, Assembler: func(args []uint16) []byte { return []byte{0b00000000} }},
+ }
+ result["HALT"] = []InstructionParams{
+ {Types: []ParamType{}, Assembler: func(args []uint16) []byte { return []byte{0b01110110} }},
+ }
+ result["STOP"] = []InstructionParams{
+ {
+ Types: []ParamType{},
+ Assembler: func(args []uint16) []byte { return []byte{0b00010000, 0b00000000} },
+ },
+ }
+ result["RLCA"] = []InstructionParams{
+ {Types: []ParamType{}, Assembler: func(args []uint16) []byte { return []byte{0b00000111} }},
+ }
+ result["RLA"] = []InstructionParams{
+ {Types: []ParamType{}, Assembler: func(args []uint16) []byte { return []byte{0b00010111} }},
+ }
+ result["RRCA"] = []InstructionParams{
+ {Types: []ParamType{}, Assembler: func(args []uint16) []byte { return []byte{0b00001111} }},
+ }
+ result["RRA"] = []InstructionParams{
+ {Types: []ParamType{}, Assembler: func(args []uint16) []byte { return []byte{0b00011111} }},
+ }
+ result["BIT"] = []InstructionParams{
+ {
+ Types: []ParamType{BitOrdinal, Reg8},
+ Assembler: func(args []uint16) []byte {
+ return []byte{0b11001011, 0b01000000 | (uint8(args[0]) << 3) | uint8(args[1])}
+ },
+ },
+ }
+ result["SET"] = []InstructionParams{
+ {
+ Types: []ParamType{BitOrdinal, Reg8},
+ Assembler: func(args []uint16) []byte {
+ return []byte{0b11001011, 0b11000000 | (uint8(args[0]) << 3) | uint8(args[1])}
+ },
+ },
+ }
+ result["RES"] = []InstructionParams{
+ {
+ Types: []ParamType{BitOrdinal, Reg8},
+ Assembler: func(args []uint16) []byte {
+ return []byte{0b11001011, 0b10000000 | (uint8(args[0]) << 3) | uint8(args[1])}
+ },
+ },
+ }
+ result["RLC"] = []InstructionParams{
+ {
+ Types: []ParamType{Reg8},
+ Assembler: func(args []uint16) []byte { return []byte{0b11001011, 0b00000000 | uint8(args[0])} },
+ },
+ }
+ result["RL"] = []InstructionParams{
+ {
+ Types: []ParamType{Reg8},
+ Assembler: func(args []uint16) []byte { return []byte{0b11001011, 0b00010000 | uint8(args[0])} },
+ },
+ }
+ result["RRC"] = []InstructionParams{
+ {
+ Types: []ParamType{Reg8},
+ Assembler: func(args []uint16) []byte { return []byte{0b11001011, 0b00001000 | uint8(args[0])} },
+ },
+ }
+ result["RR"] = []InstructionParams{
+ {
+ Types: []ParamType{Reg8},
+ Assembler: func(args []uint16) []byte { return []byte{0b11001011, 0b00011000 | uint8(args[0])} },
+ },
+ }
+ result["SLA"] = []InstructionParams{
+ {
+ Types: []ParamType{Reg8},
+ Assembler: func(args []uint16) []byte { return []byte{0b11001011, 0b00100000 | uint8(args[0])} },
+ },
+ }
+ result["SWAP"] = []InstructionParams{
+ {
+ Types: []ParamType{Reg8},
+ Assembler: func(args []uint16) []byte { return []byte{0b11001011, 0b00110000 | uint8(args[0])} },
+ },
+ }
+ result["SRA"] = []InstructionParams{
+ {
+ Types: []ParamType{Reg8},
+ Assembler: func(args []uint16) []byte { return []byte{0b11001011, 0b00101000 | uint8(args[0])} },
+ },
+ }
+ result["SRL"] = []InstructionParams{
+ {
+ Types: []ParamType{Reg8},
+ Assembler: func(args []uint16) []byte { return []byte{0b11001011, 0b00111000 | uint8(args[0])} },
+ },
+ }
+
+ return result
+}
+
+func (set InstructionSet) Parse(line string) ([]byte, error) {
+ words := strings.Fields(strings.ReplaceAll(strings.Trim(line, " \t\n"), ",", " "))
+
+ if len(words) < 1 {
+ return []uint8{}, nil
+ }
+
+ instruction, ok := set[words[0]]
+ if !ok {
+ return nil, fmt.Errorf("Unknown instruction \"%s\"", words[0])
+ }
+
+ params := words[1:]
+
+instruction_param_loop:
+ for _, instrParam := range instruction {
+ if len(instrParam.Types) != len(params) {
+ continue
+ }
+
+ parsed_params := make([]uint16, len(params))
+ for i, paramType := range instrParam.Types {
+ parsed, err := paramType(params[i])
+ if err != nil {
+ continue instruction_param_loop
+ }
+
+ parsed_params[i] = parsed
+ }
+
+ return instrParam.Assembler(parsed_params), nil
+ }
+ return nil, fmt.Errorf(
+ "Instruction \"%s\" doesn't have a parameter set that can parse \"%s\"",
+ words[0],
+ line,
+ )
+}
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..101e8d7
--- /dev/null
+++ b/main.go
@@ -0,0 +1,55 @@
+package main
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "strings"
+)
+
+func main() {
+ if len(os.Args) != 3 {
+ fmt.Fprintf(os.Stderr, "Usage: gbasm [input_file] [output_file]")
+ os.Exit(1)
+ }
+
+ input_file_name := os.Args[1]
+ output_file_name := os.Args[2]
+
+ input_file, err := os.Open(input_file_name)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error while opening input file: %s", err.Error())
+ os.Exit(1)
+ }
+
+ input, err := io.ReadAll(input_file)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error while reading input file: %s", err.Error())
+ os.Exit(1)
+ }
+
+ lines := strings.Split(string(input), "\n")
+
+ result := []byte{}
+ for _, line := range lines {
+ next_instruction, err := Instructions.Parse(line)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s", err.Error())
+ os.Exit(1)
+ }
+
+ result = append(result, next_instruction...)
+ }
+
+ output_file, err := os.Create(output_file_name)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error while opening output file: %s", err.Error())
+ os.Exit(1)
+ }
+
+ _, err = output_file.Write(result)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error while writing to output file: %s", err.Error())
+ os.Exit(1)
+ }
+}
diff --git a/parameters.go b/parameters.go
new file mode 100644
index 0000000..665c551
--- /dev/null
+++ b/parameters.go
@@ -0,0 +1,134 @@
+package main
+
+import (
+ "fmt"
+ "strconv"
+)
+
+func Reg8(param string) (uint16, error) {
+ switch param {
+ case "A":
+ return 7, nil
+ case "B":
+ return 0, nil
+ case "C":
+ return 1, nil
+ case "D":
+ return 2, nil
+ case "E":
+ return 3, nil
+ case "H":
+ return 4, nil
+ case "L":
+ return 5, nil
+ case "(HL)":
+ return 6, nil
+ }
+ return 0, fmt.Errorf("Invalid reg8")
+}
+
+func A(param string) (uint16, error) {
+ if param == "A" {
+ return 0, nil
+ }
+ return 0, fmt.Errorf("Invalid A")
+}
+
+func HL(param string) (uint16, error) {
+ if param == "HL" {
+ return 0, nil
+ }
+ return 0, fmt.Errorf("Invalid HL")
+}
+
+func SP(param string) (uint16, error) {
+ if param == "SP" {
+ return 0, nil
+ }
+ return 0, fmt.Errorf("Invalid SP")
+}
+
+func IndirectC(param string) (uint16, error) {
+ if param == "(C)" {
+ return 0, nil
+ }
+ return 0, fmt.Errorf("Invalid (C)")
+}
+
+func Reg16(param string) (uint16, error) {
+ switch param {
+ case "BC":
+ return 0, nil
+ case "DE":
+ return 1, nil
+ case "HL":
+ return 2, nil
+ case "AF":
+ return 3, nil
+ // TODO Split in two different for push and not push instructions
+ case "SP":
+ return 3, nil
+ }
+ return 0, fmt.Errorf("Invalid reg16")
+}
+
+func Raw8(param string) (uint16, error) {
+ res, err := strconv.ParseInt(param, 0, 8)
+ return uint16(res), err
+}
+
+func Raw16(param string) (uint16, error) {
+ res, err := strconv.ParseInt(param, 0, 16)
+ return uint16(res), err
+}
+
+func Reg16Indirect(param string) (uint16, error) {
+ switch param {
+ case "(BC)":
+ return 0, nil
+ case "(DE)":
+ return 1, nil
+ case "(HL+)":
+ return 2, nil
+ case "(HL-)":
+ return 3, nil
+ }
+ return 0, fmt.Errorf("Invalid reg16 indirect")
+}
+
+func Raw8Indirect(param string) (uint16, error) {
+ if len(param) < 2 || param[0] != '(' || param[len(param)-1] != ')' {
+ return 0, fmt.Errorf("Invalid raw8indirect")
+ }
+
+ res, err := strconv.ParseInt(param[1:len(param)-1], 0, 8)
+ return uint16(res), err
+}
+
+func Raw16Indirect(param string) (uint16, error) {
+ if len(param) < 2 || param[0] != '(' || param[len(param)-1] != ')' {
+ return 0, fmt.Errorf("Invalid raw16indirect")
+ }
+
+ res, err := strconv.ParseInt(param[1:len(param)-1], 0, 16)
+ return uint16(res), err
+}
+
+func Condition(param string) (uint16, error) {
+ switch param {
+ case "NZ":
+ return 0, nil
+ case "Z":
+ return 1, nil
+ case "NC":
+ return 2, nil
+ case "C":
+ return 3, nil
+ }
+ return 0, fmt.Errorf("Invalid condition")
+}
+
+func BitOrdinal(param string) (uint16, error) {
+ res, err := strconv.ParseInt(param, 0, 3)
+ return uint16(res), err
+}
diff --git a/test.gbasm b/test.gbasm
new file mode 100644
index 0000000..f64992a
--- /dev/null
+++ b/test.gbasm
@@ -0,0 +1,2 @@
+LD D, A
+LD C, B