aboutsummaryrefslogtreecommitdiff
path: root/src/Micro/Asm/Sdcc.hs
blob: a6451290fa71706d44789fa340c3d498f3765b6d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
module Micro.Asm.Sdcc where

import qualified Micro.Ast as A

toIdent :: A.Ident -> String
toIdent id = "_" ++ id

toLabel :: A.Ident -> Bool -> String
toLabel id False = toIdent id ++ "::"
toLabel id True = toIdent id ++ ":"

toData :: A.Type -> String
toData (A.Type t)
  | t == "bool" || t == "u8" || t == "s8" = ".ds 1"
  | t == "u16" || t == "s16" = ".ds 2"
  | otherwise = ".ds 2"
toData (A.FuncType _ _) = ".ds 2"

toInit :: A.Type -> A.Expr -> String
toInit (A.Type t) (A.Num v _)
  | t == "bool" || t == "u8" || t == "s8" = ".db " ++ show v
  | t == "u16" || t == "s16" = ".dw " ++ show v
  | otherwise = ".dw " ++ show v
toInit _ v = ".dw " ++ show v

header :: String -> [String]
header version = [";", "; File created by Micro v" ++ version ++ " (SDCC)", ";"]

module' :: String -> String
module' name = "\t.module " ++ name

optsdcc :: String
optsdcc = "\t.optsdcc -mz80"

area :: String -> String
area name = "\n\t.area " ++ name

globl :: String -> String
globl id = "\t.globl " ++ id

code :: [String]
code = [area "_CODE"]

data Output = Output
  { oPre :: [String],
    oInit :: [String],
    oData :: [String],
    oCode :: [String]
  }

emit :: A.Expr -> Output
emit x =
  case x of
    (A.Module name _) -> o {oPre = [module' name, optsdcc, ""]}
    (A.Func ident params ret body priv anon pos) -> do
      let out = map emit body
      let code = concat (map oCode out)
      o
        { oPre = if priv then [] else [globl $ toIdent ident],
          oCode = [toLabel ident priv] ++ code ++ (if last code /= "\tret" then ["\tret"] else [])
        }
    (A.Var id typ val priv False _) ->
      o
        { oPre = if priv then [] else [globl $ toIdent id],
          oData = [toLabel id priv, "\t" ++ toData typ],
          oInit = ["__xinit" ++ toLabel id True, "\t" ++ toInit typ val]
        }
    (A.Call (A.Variable id _) _ _) -> o {oCode = ["\tcall " ++ toIdent id]}
    (A.Return (Just value) _) -> o {oCode = ["\tld hl, ???", "\tret"]}
    (A.Return Nothing _) -> o {oCode = ["\tret"]}
    _ -> o
  where
    o = Output [] [] [] []

generate :: String -> [A.Expr] -> String
generate version ast = do
  out <- pure $ map emit ast
  pre <- pure $ concat $ map oPre out
  dat <- pure $ ["\n\t.area _DATA", "\t.area _INITIALIZED"] ++ concat (map oData out)
  code <- pure $ ["\n\t.area _CODE"] ++ concat (map oCode out)
  init <- pure $ ["\n\t.area _INITIALIZER"] ++ concat (map oInit out) ++ ["\n\t.area _GSINIT", "\t.area _GSFINAL"]
  unlines $ header version ++ pre ++ dat ++ code ++ init