diff options
author | Astatin <[email protected]> | 2024-09-19 18:55:25 +0900 |
---|---|---|
committer | Astatin <astatin@redacted> | 2024-09-19 18:55:25 +0900 |
commit | 1680bc5146b25d3631e6b7bfee3946ef59d906fd (patch) | |
tree | 5aac450b147ab4c02b51f2968caefbc343a0a577 | |
parent | 7f41367ce01fbecaec6eb5f173152a59fe55e669 (diff) |
Create 1st draft of new assembler in go
-rw-r--r-- | examples/\ | 27 | ||||
-rw-r--r-- | examples/minimal.gbasm | 37 | ||||
-rw-r--r-- | examples/minimal.rom | bin | 0 -> 363 bytes | |||
-rw-r--r-- | examples/zoom.gbasm | 125 | ||||
-rw-r--r-- | examples/zoom.rom | bin | 0 -> 768 bytes | |||
-rw-r--r-- | examples/zoom.rom.sav | bin | 0 -> 32768 bytes | |||
-rw-r--r-- | go.mod | 3 | ||||
-rw-r--r-- | instructions.go | 432 | ||||
-rw-r--r-- | main.go | 55 | ||||
-rw-r--r-- | parameters.go | 134 | ||||
-rw-r--r-- | test.gbasm | 2 |
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 Binary files differnew file mode 100644 index 0000000..271c355 --- /dev/null +++ b/examples/minimal.rom 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 Binary files differnew file mode 100644 index 0000000..f6b1239 --- /dev/null +++ b/examples/zoom.rom diff --git a/examples/zoom.rom.sav b/examples/zoom.rom.sav Binary files differnew file mode 100644 index 0000000..12f3be4 --- /dev/null +++ b/examples/zoom.rom.sav @@ -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, + ) +} @@ -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 |