Add Odin templating and Z80Disasm

This commit is contained in:
Paul W. 2024-09-11 21:05:27 -04:00
commit e49ecd139a
8 changed files with 687 additions and 0 deletions

61
odin-templating/README.md Normal file
View File

@ -0,0 +1,61 @@
# HTML Templates in Odin
```odin
page := html()
{head()
{title("HTML Title")}
}
{body()
{div()
class("container")
{div()
class("article")
{h1("Hello, world!")}
{h6("Goodbye, world!")}
{
{div()
attr("aria-data", "idk!")
text("Yo, what's up!")
}
{div()
{a("don't be eeeviiiilll", "http://google.com")}
{a("This link has a class called 'button'")
class("button")}
}
}
}
{img("background.png", "green texture")
class("img")
}
{div()
id("lol")
class("hello")
}
{text("Random text")}
{text("")}
}
}
```
<html>
<head>
<title>HTML Title</title>
</head>
<body>
<div class="container">
<div class="article">
<h1>Hello, world!</h1>
<h6>Goodbye, world!</h6>
<div aria-data="idk!">Yo, what's up!</div>
<div>
<a href="http://google.com">don't be eeeviiiilll</a>
<a class="button" href="">This link has a class called 'button'</a>
</div>
</div>
<img class="img" alt="green texture" src="background.png">
<div class="hello" id="lol"></div>
Random text
</div>
</body>
</html>

View File

@ -0,0 +1,237 @@
package html_builder
import "core:fmt"
import "core:slice"
import "core:strings"
Element :: struct {
tag_name: string,
id: string,
children: [dynamic]Node,
attributes: map[string]string,
class_list: [dynamic]string,
}
Node :: union {
Element,
string,
}
empty_tags: map[string]bool = {
"img" = true,
"br" = true,
"hr" = true,
}
indent :: proc(sb: ^strings.Builder, depth: int) {
for i in 0 ..< depth {
fmt.sbprintf(sb, "\t")
}
}
element_to_string :: proc(sb: ^strings.Builder, e: Element, depth: int = 0) -> string {
indent(sb, depth)
fmt.sbprintf(sb, "<%s", e.tag_name)
for name, attr in e.attributes {
fmt.sbprintf(sb, " %s=\"%s\"", name, attr)
}
fmt.sbprintf(sb, ">")
children_len := len(e.children)
if children_len == 1 {
switch ch in e.children[0] {
case string:
fmt.sbprintf(sb, "%s", ch)
case Element:
fmt.sbprintf(sb, "\n")
element_to_string(sb, ch, depth + 1)
fmt.sbprintf(sb, "\n")
indent(sb, depth)
}
}
if children_len > 1 {
for child, idx in e.children {
fmt.sbprintf(sb, "\n")
switch ch in child {
case string:
indent(sb, depth + 1)
fmt.sbprintf(sb, "%s", ch)
case Element:
element_to_string(sb, ch, depth + 1)
}
}
fmt.sbprintf(sb, "\n")
indent(sb, depth)
}
if !empty_tags[e.tag_name] do fmt.sbprintf(sb, "</%s>", e.tag_name)
return strings.to_string(sb^)
}
parent_element: ^Element
elements_by_id: map[string]^Element
elements_by_classes: map[string][dynamic]^Element
_element_scope_start :: proc(tag_name: string, id: string = "") -> (previous_parent: ^Element) {
children := &parent_element.children
new_idx := len(children^)
new_element := Element {
tag_name = tag_name,
children = make([dynamic]Node),
}
append(children, new_element)
previous_parent = parent_element
parent_element = &children[new_idx].(Element)
return previous_parent
}
_element_scope_end :: proc(previous_parent: ^Element) {
parent_element = previous_parent
}
// @(deferred_out = free_html)
html :: proc() -> ^Element {
parent_element = new(Element)
parent_element.tag_name = "html"
parent_element.children = make([dynamic]Node)
return parent_element
}
text :: proc(txt: string) {
append(&parent_element.children, txt)
}
attr :: proc(name: string, value: string) {
if name == "class" {
class(value)
return
}
map_insert(&parent_element.attributes, name, value)
}
id :: proc(id: string) {
if id != "" {
parent_element.id = id
parent_element.attributes["id"] = id
elements_by_id[id] = parent_element
}
}
class :: proc(class_names: string) {
classes: [dynamic]string
for class in strings.split(class_names, " ") {
_, found := slice.linear_search(parent_element.class_list[:], class)
if elements_by_classes[class] == nil do elements_by_classes[class] = make([dynamic]^Element)
if !found {
append(&classes, class)
append(&elements_by_classes[class], parent_element)
}
}
append(&parent_element.class_list, ..classes[:])
map_insert(
&parent_element.attributes,
"class",
strings.join(parent_element.class_list[:], " "),
)
}
free_html :: proc(el: ^Element) {
defer parent_element = nil
handle_children :: proc(children: [dynamic]Node) {
for e in children {
_e, ok := e.(Element)
if ok do handle_children(_e.children)
}
delete(children)
}
handle_children(el.children)
free(el)
}
@(deferred_out = _element_scope_end)
body :: proc(id: string = "") -> (previous_parent: ^Element) {
return _element_scope_start("body", id)
}
@(deferred_out = _element_scope_end)
head :: proc(id: string = "") -> (previous_parent: ^Element) {
return _element_scope_start("head", id)
}
@(deferred_out = _element_scope_end)
title :: proc(txt: string = "") -> (previous_parent: ^Element) {
el := _element_scope_start("title")
text(txt)
return el
}
@(deferred_out = _element_scope_end)
h1 :: proc(txt: string = "") -> (previous_parent: ^Element) {
el := _element_scope_start("h1")
text(txt)
return el
}
@(deferred_out = _element_scope_end)
h2 :: proc(txt: string = "") -> (previous_parent: ^Element) {
el := _element_scope_start("h2")
text(txt)
return el
}
@(deferred_out = _element_scope_end)
h3 :: proc(txt: string = "") -> (previous_parent: ^Element) {
el := _element_scope_start("h3")
text(txt)
return el
}
@(deferred_out = _element_scope_end)
h4 :: proc(txt: string = "") -> (previous_parent: ^Element) {
el := _element_scope_start("h4")
text(txt)
return el
}
@(deferred_out = _element_scope_end)
h5 :: proc(txt: string = "") -> (previous_parent: ^Element) {
el := _element_scope_start("h5")
text(txt)
return el
}
@(deferred_out = _element_scope_end)
h6 :: proc(txt: string = "") -> (previous_parent: ^Element) {
el := _element_scope_start("h6")
text(txt)
return el
}
@(deferred_out = _element_scope_end)
div :: proc(id: string = "") -> (previous_parent: ^Element) {
previous_parent = _element_scope_start("div", id)
return previous_parent
}
@(deferred_out = _element_scope_end)
img :: proc(src: string = "", alt: string = "") -> (previous_parent: ^Element) {
previous_parent = _element_scope_start("img")
parent_element.children = nil
parent_element.attributes["src"] = src
if alt != "" do parent_element.attributes["alt"] = alt
return previous_parent
}
@(deferred_out = _element_scope_end)
a :: proc(txt: string, href: string = "") -> (previous_parent: ^Element) {
previous_parent = _element_scope_start("a")
append(&parent_element.children, txt)
parent_element.attributes["href"] = href
return previous_parent
}

46
odin-templating/main.odin Normal file
View File

@ -0,0 +1,46 @@
package html_builder
import "core:fmt"
import "core:strings"
main :: proc() {
page := html()
{head()
{title("HTML Title")}
}
{body()
{div()
class("container")
{div()
class("article")
{h1("Hello, world!")}
{h6("Goodbye, world!")}
{
{div()
attr("aria-data", "idk!")
text("Yo, what's up!")
}
{div()
{a("don't be eeeviiiilll", "http://google.com")}
{a("This link has a class called 'button'")
class("button")}
}
}
}
{img("background.png", "green texture")
class("img")
}
{div()
id("lol")
class("hello")
}
{text("Random text")}
}
}
defer free_html(page)
sb: strings.Builder
fmt.println(element_to_string(&sb, page^))
}

8
z80_disasm/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
bin
obj
.DS_Store
cache
.cache
.dll
.exe
.vs

49
z80_disasm/README.md Normal file
View File

@ -0,0 +1,49 @@
# A Z80 Disassembler
The purpose of this project is to disassemble Z80 machine code as usable string.
## TODO
- [x] Basic Disassembly
- [ ] Extended Instructions Support
- [ ] IX Instructions Support
- [ ] IY Instructions Support
- [ ] Unit Testing
## About the Z80 Instruction Set
The Z80 CPU has 158 different instruction types including the 78 8080A instructions.
### Instruction Types
They are categorized into:
- Load and Exchange
- Block Transfer and Search
- Arithmetic and Logical
- Rotate and Shift
- Bit Manipulation
- Jump, Call, and Return
- Input/Output
- CPU Control
### Addressing Modes
- Immediate
- Immediate Extended
- Modified Page Zero
- Relative
- Extended
- Indexed Addressing
- Register
- Implied
- Register Indirect
- Bit
or a combination of any two modes.
Registers
Accumulators | Flags
------ | ------
A | F
B | C
D | E
H | L

22
z80_disasm/src/Test.cs Normal file
View File

@ -0,0 +1,22 @@
using Z80;
class Test {
// not sure what i was thinking when i wrote this
static void Main(string[] args)
{
Disasm disasm = new Disasm();
byte[] x = new byte[3000];
for (uint i = 0; i < 3000; i++)
{
x[i] = (byte) (i % 256);
}
disasm.Bytes = x;
var watch = System.Diagnostics.Stopwatch.StartNew();
System.Console.WriteLine(disasm.Bytes.Length);
System.Console.WriteLine(disasm.Disassemble());
watch.Stop();
var elapsedMs = watch.ElapsedMilliseconds;
System.Console.WriteLine("Time elapsed: " + elapsedMs + "ms.");
}
}

255
z80_disasm/src/Z80Disasm.cs Normal file
View File

@ -0,0 +1,255 @@
namespace Z80
{
public class Disasm
{
public byte[] Bytes
{
get; set;
}
private uint labelCounter = 0;
private uint programCounter = 0;
private byte currentOp = 0;
private string currentInstrName;
private string currentPredicate;
public string Disassemble()
{
if (Bytes == null)
{
throw new System.NullReferenceException();
}
string ret = "";
for (int i = 0; i < Bytes.Length; i++)
{
ret += DisassembleInstruction() + "\n";
}
return ret;
}
public string DisassembleInstruction()
{
int err;
if (programCounter + 1 > Bytes.Length)
return "EOS";
err = DisasmInstructionName();
if (programCounter + 1 > Bytes.Length)
return "EOS";
if (err == -2)
DisasmBitInstructionNames();
DisasmPredicate();
return currentInstrName + " " + currentPredicate;
}
private int DisasmInstructionName()
{
currentOp = Bytes[programCounter++];
currentInstrName = "<?>";
switch (currentOp)
{
case 0:
currentInstrName = "NOP";
break;
case 0xCB:
currentInstrName = "BIT INSTRUCTIONS";
return -2;
case 0xDD:
currentInstrName = "IX INSTRUCTIONS";
return -3;
case 0xED:
currentInstrName = "EXTENDED INSTRUCTIONS";
return -4;
default:
break;
}
/*
The load instruction
*/
if (currentOp == 0xF9)
{
currentInstrName = "LD";
}
switch (currentOp & 0xF0)
{
case 0x40:
case 0x50:
case 0x60:
case 0x70:
if (currentOp != 0x76)
currentInstrName = "LD";
else
currentInstrName = "HALT";
break;
default:
break;
}
switch ((currentOp) & 0x0F)
{
case 0x01:
case 0x02:
case 0x06:
case 0x0A:
case 0x0E:
if (currentOp < 0x80)
currentInstrName = "LD";
break;
default:
break;
}
if (currentOp >= 0x80 && currentOp <= 0x8F || currentOp == 0xC6)
currentInstrName = "ADD";
if (currentOp >= 0x90 && currentOp <= 0x9F || currentOp == 0xD6)
currentInstrName = "SUB";
if (currentOp >= 0xA0 && currentOp <= 0xAF || currentOp == 0xE6)
currentInstrName = "AND";
if (currentOp >= 0xB0 && currentOp <= 0xBF || currentOp == 0xF6)
currentInstrName = "OR";
if (currentOp >= 0x88 && currentOp <= 0x8F || currentOp == 0xCE)
currentInstrName = "ADC";
if (currentOp >= 0x98 && currentOp <= 0x9F || currentOp == 0xDE)
currentInstrName = "SBC";
if (currentOp >= 0xA8 && currentOp <= 0xAF || currentOp == 0xEE)
currentInstrName = "XOR";
if (currentOp >= 0xB8 && currentOp <= 0xBF || currentOp == 0xFE)
currentInstrName = "CP";
return 0;
}
private int DisasmBitInstructionNames()
{
byte instr = Bytes[programCounter++];
if ((instr & 0x30) != 0)
{
if (instr <= 0x07)
currentInstrName = "RLC";
else if (instr <= 0x0F)
currentInstrName = "RRC";
else if (instr <= 0x17)
currentInstrName = "RL";
else if (instr <= 0x1F)
currentInstrName = "RR";
else if (instr <= 0x27)
currentInstrName = "SLA";
else if (instr <= 0x2F)
currentInstrName = "SRA";
else if (instr <= 0x37)
currentInstrName = "SSL";
else if (instr <= 0x3F)
currentInstrName = "SRL";
}
switch (instr & 0xF0)
{
case 0x40:
case 0x50:
case 0x60:
case 0x70:
currentInstrName = "BIT";
break;
case 0x80:
case 0x90:
case 0xA0:
case 0xB0:
currentInstrName = "RES";
break;
case 0xC0:
case 0xD0:
case 0xE0:
case 0xF0:
currentInstrName = "SET";
break;
default:
break;
}
return 0;
}
public int DisasmPredicate()
{
currentPredicate = "";
if (currentOp >= 0x40 && currentOp <= 0x47)
{
currentPredicate = "b, ";
}
if (currentOp >= 0x50 && currentOp <= 0x57)
{
currentPredicate = "d, ";
}
if (currentOp >= 0x60 && currentOp <= 0x67)
{
currentPredicate = "h, ";
}
if (currentOp >= 0x70 && currentOp <= 0x77)
{
currentPredicate = "(hl), ";
}
if (currentOp >= 0x48 && currentOp <= 0x4F)
{
currentPredicate = "c, ";
}
if (currentOp >= 0x58 && currentOp <= 0x5F)
{
currentPredicate = "e, ";
}
if (currentOp >= 0x68 && currentOp <= 0x6F)
{
currentPredicate = "l, ";
}
if (currentOp >= 0x78 && currentOp <= 0x7F)
{
currentPredicate = "a, ";
}
if (currentOp >= 0x40 && currentOp <= 0xBF)
{
switch (currentOp & 0x0F)
{
case 0x7:
case 0xF:
currentPredicate += "a";
break;
case 0x0:
case 0x8:
currentPredicate += "b";
break;
case 0x1:
case 0x9:
currentPredicate += "c";
break;
case 0x2:
case 0xA:
currentPredicate += "d";
break;
case 0x3:
case 0xB:
currentPredicate += "e";
break;
case 0x4:
case 0xC:
currentPredicate += "h";
break;
case 0x5:
case 0xD:
currentPredicate += "l";
break;
case 0x6:
case 0xE:
currentPredicate += "(hl)";
break;
default:
break;
}
}
return 0;
}
}
}

View File

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>z80_disasm</RootNamespace>
</PropertyGroup>
</Project>