Implemented instruction lists
This commit is contained in:
parent
4967719902
commit
a54c62f6e0
12
README.md
12
README.md
@ -4,8 +4,8 @@ A simple programming language.
|
||||
|
||||
## Features
|
||||
|
||||
* 🔥 Fast compilation
|
||||
* 📦 Small binaries
|
||||
* Fast compilation
|
||||
* Small binaries
|
||||
|
||||
## Installation
|
||||
|
||||
@ -17,10 +17,18 @@ go build
|
||||
|
||||
## Usage
|
||||
|
||||
Build a Linux ELF executable from `examples/hello`:
|
||||
|
||||
```shell
|
||||
./q build examples/hello
|
||||
```
|
||||
|
||||
Run the generated executable:
|
||||
|
||||
```shell
|
||||
./examples/hello/hello
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Please see the [license documentation](https://akyoto.dev/license).
|
||||
|
23
src/asm/Base.go
Normal file
23
src/asm/Base.go
Normal file
@ -0,0 +1,23 @@
|
||||
package asm
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"git.akyoto.dev/cli/q/src/asm/x64"
|
||||
)
|
||||
|
||||
// Base represents the data that is common among all instructions.
|
||||
type Base struct {
|
||||
Mnemonic Mnemonic
|
||||
}
|
||||
|
||||
func (x *Base) Write(w io.ByteWriter) {
|
||||
switch x.Mnemonic {
|
||||
case SYSCALL:
|
||||
x64.Syscall(w)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Base) String() string {
|
||||
return x.Mnemonic.String()
|
||||
}
|
8
src/asm/Instruction.go
Normal file
8
src/asm/Instruction.go
Normal file
@ -0,0 +1,8 @@
|
||||
package asm
|
||||
|
||||
import "io"
|
||||
|
||||
type Instruction interface {
|
||||
Write(io.ByteWriter)
|
||||
String() string
|
||||
}
|
47
src/asm/InstructionList.go
Normal file
47
src/asm/InstructionList.go
Normal file
@ -0,0 +1,47 @@
|
||||
package asm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.akyoto.dev/cli/q/src/register"
|
||||
)
|
||||
|
||||
type InstructionList struct {
|
||||
Instructions []Instruction
|
||||
}
|
||||
|
||||
// Finalize generates the final assembly code.
|
||||
func (list *InstructionList) Finalize() *Result {
|
||||
final := Result{}
|
||||
|
||||
for _, instr := range list.Instructions {
|
||||
instr.Write(&final.Code)
|
||||
fmt.Println(instr.String())
|
||||
}
|
||||
|
||||
return &final
|
||||
}
|
||||
|
||||
func (list *InstructionList) MoveRegisterNumber(reg register.Register, number uint64) {
|
||||
list.addRegisterNumber(MOV, reg, number)
|
||||
}
|
||||
|
||||
func (list *InstructionList) Syscall() {
|
||||
list.add(SYSCALL)
|
||||
}
|
||||
|
||||
// add adds an instruction without any operands.
|
||||
func (list *InstructionList) add(mnemonic Mnemonic) {
|
||||
list.Instructions = append(list.Instructions, &Base{Mnemonic: mnemonic})
|
||||
}
|
||||
|
||||
// addRegisterNumber adds an instruction using a register and a number.
|
||||
func (list *InstructionList) addRegisterNumber(mnemonic Mnemonic, reg register.Register, number uint64) {
|
||||
list.Instructions = append(list.Instructions, &RegisterNumber{
|
||||
Base: Base{
|
||||
Mnemonic: mnemonic,
|
||||
},
|
||||
Register: reg,
|
||||
Number: number,
|
||||
})
|
||||
}
|
21
src/asm/Mnemonic.go
Normal file
21
src/asm/Mnemonic.go
Normal file
@ -0,0 +1,21 @@
|
||||
package asm
|
||||
|
||||
type Mnemonic uint8
|
||||
|
||||
const (
|
||||
NONE Mnemonic = iota
|
||||
MOV
|
||||
SYSCALL
|
||||
)
|
||||
|
||||
func (m Mnemonic) String() string {
|
||||
switch m {
|
||||
case MOV:
|
||||
return "mov"
|
||||
|
||||
case SYSCALL:
|
||||
return "syscall"
|
||||
}
|
||||
|
||||
return "NONE"
|
||||
}
|
26
src/asm/RegisterNumber.go
Normal file
26
src/asm/RegisterNumber.go
Normal file
@ -0,0 +1,26 @@
|
||||
package asm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"git.akyoto.dev/cli/q/src/asm/x64"
|
||||
"git.akyoto.dev/cli/q/src/register"
|
||||
)
|
||||
|
||||
type RegisterNumber struct {
|
||||
Base
|
||||
Register register.Register
|
||||
Number uint64
|
||||
}
|
||||
|
||||
func (x *RegisterNumber) Write(w io.ByteWriter) {
|
||||
switch x.Mnemonic {
|
||||
case MOV:
|
||||
x64.MoveRegNum32(w, uint8(x.Register), uint32(x.Number))
|
||||
}
|
||||
}
|
||||
|
||||
func (x *RegisterNumber) String() string {
|
||||
return fmt.Sprintf("%s %s, %x", x.Mnemonic, x.Register, x.Number)
|
||||
}
|
@ -2,7 +2,7 @@ package asm
|
||||
|
||||
import "bytes"
|
||||
|
||||
type Assembler struct {
|
||||
type Result struct {
|
||||
Code bytes.Buffer
|
||||
Data bytes.Buffer
|
||||
}
|
@ -2,7 +2,7 @@ package x64
|
||||
|
||||
import "io"
|
||||
|
||||
// AppendUint32 appends a 32 bit number in Little Endian to the given writer.
|
||||
// AppendUint32 appends a 32 bit integer in Little Endian to the given writer.
|
||||
func AppendUint32(w io.ByteWriter, number uint32) {
|
||||
w.WriteByte(byte(number))
|
||||
w.WriteByte(byte(number >> 8))
|
||||
|
@ -4,7 +4,8 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
func MoveRegNum32(w io.ByteWriter, register byte, number uint32) {
|
||||
// MoveRegNum32 moves a 32 bit integer into the given register.
|
||||
func MoveRegNum32(w io.ByteWriter, register uint8, number uint32) {
|
||||
w.WriteByte(0xb8 + register)
|
||||
AppendUint32(w, number)
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// Syscall is the primary way to communicate with the OS kernel.
|
||||
func Syscall(w io.ByteWriter) {
|
||||
w.WriteByte(0x0f)
|
||||
w.WriteByte(0x05)
|
||||
|
@ -7,7 +7,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"git.akyoto.dev/cli/q/src/asm"
|
||||
"git.akyoto.dev/cli/q/src/asm/x64"
|
||||
"git.akyoto.dev/cli/q/src/directory"
|
||||
"git.akyoto.dev/cli/q/src/elf"
|
||||
"git.akyoto.dev/cli/q/src/errors"
|
||||
@ -40,26 +39,26 @@ func (build *Build) Run() error {
|
||||
return err
|
||||
}
|
||||
|
||||
list := asm.InstructionList{}
|
||||
|
||||
list.MoveRegisterNumber(register.Syscall0, syscall.Write)
|
||||
list.MoveRegisterNumber(register.Syscall1, 1)
|
||||
list.MoveRegisterNumber(register.Syscall2, 0x4000a2)
|
||||
list.MoveRegisterNumber(register.Syscall3, 6)
|
||||
list.Syscall()
|
||||
|
||||
list.MoveRegisterNumber(register.Syscall0, syscall.Exit)
|
||||
list.MoveRegisterNumber(register.Syscall1, 0)
|
||||
list.Syscall()
|
||||
|
||||
result := list.Finalize()
|
||||
result.Data.WriteString("Hello\n")
|
||||
|
||||
if !build.WriteExecutable {
|
||||
return nil
|
||||
}
|
||||
|
||||
final := asm.Assembler{}
|
||||
code := &final.Code
|
||||
data := &final.Data
|
||||
|
||||
x64.MoveRegNum32(code, register.Syscall0, syscall.Write)
|
||||
x64.MoveRegNum32(code, register.Syscall1, 1)
|
||||
x64.MoveRegNum32(code, register.Syscall2, 0x4000a2)
|
||||
x64.MoveRegNum32(code, register.Syscall3, 6)
|
||||
x64.Syscall(code)
|
||||
|
||||
x64.MoveRegNum32(code, register.Syscall0, syscall.Exit)
|
||||
x64.MoveRegNum32(code, register.Syscall1, 0)
|
||||
x64.Syscall(code)
|
||||
|
||||
data.WriteString("Hello\n")
|
||||
return writeToDisk(build.Executable(), code.Bytes(), data.Bytes())
|
||||
return writeToDisk(build.Executable(), result.Code.Bytes(), result.Data.Bytes())
|
||||
}
|
||||
|
||||
// Compile compiles all the functions.
|
||||
|
@ -1,7 +1,11 @@
|
||||
package register
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Register uint8
|
||||
|
||||
const (
|
||||
R0 = iota
|
||||
R0 Register = iota
|
||||
R1
|
||||
R2
|
||||
R3
|
||||
@ -18,3 +22,7 @@ const (
|
||||
R14
|
||||
R15
|
||||
)
|
||||
|
||||
func (r Register) String() string {
|
||||
return fmt.Sprintf("r%d", r)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user