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
|
## Features
|
||||||
|
|
||||||
* 🔥 Fast compilation
|
* Fast compilation
|
||||||
* 📦 Small binaries
|
* Small binaries
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@ -17,10 +17,18 @@ go build
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
Build a Linux ELF executable from `examples/hello`:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
./q build examples/hello
|
./q build examples/hello
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Run the generated executable:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
./examples/hello/hello
|
||||||
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Please see the [license documentation](https://akyoto.dev/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"
|
import "bytes"
|
||||||
|
|
||||||
type Assembler struct {
|
type Result struct {
|
||||||
Code bytes.Buffer
|
Code bytes.Buffer
|
||||||
Data bytes.Buffer
|
Data bytes.Buffer
|
||||||
}
|
}
|
@ -2,7 +2,7 @@ package x64
|
|||||||
|
|
||||||
import "io"
|
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) {
|
func AppendUint32(w io.ByteWriter, number uint32) {
|
||||||
w.WriteByte(byte(number))
|
w.WriteByte(byte(number))
|
||||||
w.WriteByte(byte(number >> 8))
|
w.WriteByte(byte(number >> 8))
|
||||||
|
@ -4,7 +4,8 @@ import (
|
|||||||
"io"
|
"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)
|
w.WriteByte(0xb8 + register)
|
||||||
AppendUint32(w, number)
|
AppendUint32(w, number)
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Syscall is the primary way to communicate with the OS kernel.
|
||||||
func Syscall(w io.ByteWriter) {
|
func Syscall(w io.ByteWriter) {
|
||||||
w.WriteByte(0x0f)
|
w.WriteByte(0x0f)
|
||||||
w.WriteByte(0x05)
|
w.WriteByte(0x05)
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.akyoto.dev/cli/q/src/asm"
|
"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/directory"
|
||||||
"git.akyoto.dev/cli/q/src/elf"
|
"git.akyoto.dev/cli/q/src/elf"
|
||||||
"git.akyoto.dev/cli/q/src/errors"
|
"git.akyoto.dev/cli/q/src/errors"
|
||||||
@ -40,26 +39,26 @@ func (build *Build) Run() error {
|
|||||||
return err
|
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 {
|
if !build.WriteExecutable {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
final := asm.Assembler{}
|
return writeToDisk(build.Executable(), result.Code.Bytes(), result.Data.Bytes())
|
||||||
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())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile compiles all the functions.
|
// Compile compiles all the functions.
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
package register
|
package register
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type Register uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
R0 = iota
|
R0 Register = iota
|
||||||
R1
|
R1
|
||||||
R2
|
R2
|
||||||
R3
|
R3
|
||||||
@ -18,3 +22,7 @@ const (
|
|||||||
R14
|
R14
|
||||||
R15
|
R15
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (r Register) String() string {
|
||||||
|
return fmt.Sprintf("r%d", r)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user