Skip to content

Commit 26a3903

Browse files
committed
Now you can assemble assembly code to .mif file
Right now we just start writing from memory cell 0, it would be useful to be able to org to different cells and start writing from that address instead.
1 parent 9de726c commit 26a3903

3 files changed

Lines changed: 132 additions & 61 deletions

File tree

Main.hs

Lines changed: 126 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,30 @@ import Data.Char
1010
import Data.Word
1111
import Data.Maybe
1212

13-
{-
14-
NOOP
15-
ADD ACC, Mem[Addr]
16-
SUB ACC, Mem[Addr]
17-
NOT ACC
18-
AND ACC, Mem[Addr]
19-
CMP ACC, Mem[Addr]
20-
LB ACC, Mem[Addr]
21-
LBI ACC, Mem[Mem[Addr]]
22-
SB Mem[Addr], ACC
23-
SBI Mem[Mem[Addr]], ACC
24-
IN Mem[Addr], IO_BUS
25-
JA Addr
26-
J Offset
27-
JEQ Offset
28-
JNE Offset
29-
DS
30-
-}
13+
help = "Usage: ./Main [-h|--help] input_file\n\n"
14+
++ "Syntax cheatsheat: First column shows valid assembly operations.\n"
15+
++ "Addr ::= hex | bin | oct | naturalnumber\n"
16+
++ "Offset ::= int\n"
17+
++ "literal prefixes: bin = `0b`, hex = `0x`, oct = `0o`\n"
18+
++ "Use `//` for line comments.\n"
19+
++ unlines (map ('\t':) [
20+
"NOOP No Operation Do Nothing",
21+
"ADD ACC, M[Addr] Add ACC = ACC + Memory[Addr]; Set C and Z flags",
22+
"SUB ACC, M[Addr] Subtract ACC = ACC - Memory[Addr]; Set C and Z flags",
23+
"NOT ACC Logical NOT ACC = ACC’; Set Z flag",
24+
"AND ACC, M[Addr] Logical AND ACC = ACC & Memory[Addr]; Set Z flag",
25+
"CMP ACC, M[Addr] Compare if (ACC == Memory[Addr]): E flag=1 else: E flag=0",
26+
"LB ACC, M[Addr] Load Byte ACC = Memory[Addr]",
27+
"LBI ACC, M[M[Addr]] Load Byte Index ACC = Memory[Memory[Addr]]",
28+
"SB M[Addr], ACC Store Byte Memory[Addr]=ACC",
29+
"SBI M[M[Addr]], ACC Store Byte Index Memory[Memory[Addr]]=ACC",
30+
"IN M[Addr], IO_BUS Input Memory[Addr] = Value at IO_BUS",
31+
"JA Addr Jump Address PC = Addr",
32+
"J Offset Jump PC = (PC+1) ± Offset",
33+
"JEQ Offset Jump Equal if (E flag == 1): PC = (PC+1) ± Offset",
34+
"JNE Offset Jump Not Equal if (E flag == 0): PC = (PC+1) ± Offset",
35+
"DS Display DS (display register) = ACC"
36+
])
3137

3238
data Reg = ACC deriving Show
3339
data IOBus = IOBus deriving Show
@@ -42,7 +48,7 @@ instance Show AddrAddr where
4248
show (AddrAddr x) = "M[M["++show x++"]]"
4349

4450
instance Show Offset where
45-
show (Offset x) = "±"++show x
51+
show (Offset x) = "+"++show x
4652

4753
data Opcode = NOOP
4854
| ADD Reg Addr
@@ -83,8 +89,9 @@ opcodes = [
8389
]
8490

8591
upperLowerToken :: Parser String
86-
upperLowerToken = do xs <- some alphanum
87-
guard (all isUpper xs || all isLower xs)
92+
upperLowerToken = do xs <- some (alphanum <|> char '_')
93+
let rs = filter isAlpha xs
94+
guard (all isUpper rs || all isLower rs)
8895
space
8996
return xs
9097

@@ -99,61 +106,88 @@ reg = do r <- upperLowerToken
99106
then return ACC
100107
else error "unknown reg"
101108

102-
readHex :: String -> Int
103-
readHex = sum . zipWith (*) [1,16..] . reverse . ys
109+
-- | expects a base under 36?? and that the string only includes valid digits
110+
-- that are allowed in the base
111+
readBase :: Int -> String -> Int
112+
readBase base = sum . zipWith (\i d -> d*base^i) [0..] . reverse . ys
104113
where
105114
ys = map (\x -> if x `elem` ['0'..'9'] then
106115
ord x - ord '0'
107116
else
108-
ord x - ord 'a' + 10)
117+
ord (toLower x) - ord 'a' + 10)
109118

110-
--hex = do { string "0x"; xs <- some (sat (`elem` (['0'..'9']++['a'..'f']) . toLower)); return (readHex xs)}
111-
--bin = do { string "0b"; nat }
119+
hex , bin, oct :: Parser Int
120+
hex = do string "0x"
121+
xs <- some (sat (\x -> toLower x `elem` "0123456789abcdef"))
122+
return (readBase 16 xs)
123+
bin = do string "0b"
124+
xs <- some (sat (\x -> toLower x `elem` "01"))
125+
return (readBase 2 xs)
126+
oct = do string "0o"
127+
xs <- some (sat (\x -> toLower x `elem` "01234567"))
128+
return (readBase 8 xs)
112129

113-
-- TODO: switch with addr, change addraddr to mememem
114-
--mem = Addr . fromIntegral <$> nat
115-
-- <|> Addr . fromIntegral <$> hex
130+
isWord8 :: Int -> Bool
131+
isWord8 n
132+
| n > 0 = fromIntegral (minBound :: Word8) <= n
133+
&& n <= fromIntegral (maxBound :: Word8)
134+
| otherwise = fromIntegral (minBound :: Word8) - 128 <= n
135+
&& n <= fromIntegral (maxBound :: Word8) - 128
116136

117137
addr :: Parser Addr
118-
addr = do char 'M'
138+
addr = do string "M["
119139
space
120-
char '['
121-
space
122-
n <- nat
140+
n <- bin <|> hex <|> oct <|> nat
141+
guard (isWord8 n)
123142
space
124143
char ']'
125144
space
126145
return (Addr . fromIntegral $ n)
127146

128147
addraddr :: Parser AddrAddr
129-
addraddr = AddrAddr . fromIntegral <$> nat
148+
addraddr = do string "M[M["
149+
space
150+
n <- hex <|> bin <|> oct <|> nat
151+
guard (isWord8 n)
152+
space
153+
string "]]"
154+
space
155+
return (AddrAddr . fromIntegral $ n)
156+
157+
addrLiteral :: Parser Addr
158+
addrLiteral = Addr . fromIntegral <$> do {n <- hex <|> bin <|> oct <|> nat;
159+
guard (isWord8 n);
160+
return n}
130161

131162
offset :: Parser Offset
132-
offset = Offset . fromIntegral <$> nat
163+
offset = Offset . fromIntegral <$> do { n <- int;
164+
guard (isWord8 n);
165+
return n }
133166

134167
ioBus :: Parser IOBus
135168
ioBus = do b <- upperLowerToken
136169
if map toUpper b == "IO_BUS"
137170
then return IOBus
138-
else error "unknown IO_Bus"
171+
else error $ "unknown IO_Bus" ++ show b
139172

140173
newline :: Parser Char
141174
newline = do { space; char '\n' }
175+
<|> do { space; string "\r\n"; return '\n'}
142176

143177
noop, add, sub, nott, andd, cmp, lb, lbi, sb, sbi, inn, ja, j, jeq, jne, ds :: Parser Opcode
144178
noop = return NOOP
145-
add = do { r <- reg; space; char ','; space; ADD r <$> addr; }
146-
sub = do { r <- reg; space; char ','; space; SUB r <$> addr; }
179+
add = do { r <- reg ; space ; char ',' ; space ; ADD r <$> addr }
180+
sub = do { r <- reg ; space ; char ',' ; space ; SUB r <$> addr }
147181
nott = NOT <$> reg
148-
andd = do { r <- reg; space; char ','; space; AND r <$> addr; }
149-
cmp = do { r <- reg; space; char ','; space; CMP r <$> addr; }
150-
lb = do { r <- reg; space; char ','; space; LB r <$> addr; }
151-
lbi = do { r <- reg; space; char ','; space; LBI r <$> addraddr; }
152-
sb = do { a <- addr; space; char ','; space; SB a <$> reg; }
153-
sbi = do { a <- addraddr; space; char ','; SBI a <$> reg; }
154-
inn = do { a <- addr; space; char ','; IN a <$> ioBus; }
155-
ja = JA <$> addr
156-
j = J <$> offset
182+
andd = do { r <- reg ; space ; char ',' ; space ; AND r <$> addr }
183+
cmp = do { r <- reg ; space ; char ',' ; space ; CMP r <$> addr }
184+
lb = do { r <- reg ; space ; char ',' ; space ; LB r <$> addr }
185+
lbi = do { r <- reg ; space ; char ',' ; space ; LBI r <$> addraddr }
186+
sb = do { a <- addr ; space ; char ',' ; space ; SB a <$> reg }
187+
sbi = do { a <- addraddr ; space ; char ',' ; space ; SBI a <$> reg }
188+
inn = do { a <- addr ; space ; char ',' ; space ; IN a <$> ioBus }
189+
ja = JA <$> addrLiteral
190+
j = J <$> offset
157191
jeq = JEQ <$> offset
158192
jne = JNE <$> offset
159193
ds = return DS
@@ -164,13 +198,13 @@ lineComment = do space
164198
xs <- many (sat (/= '\n'))
165199
return (c++xs)
166200

201+
lineWithComment :: Parser (Maybe Opcode)
167202
lineWithComment = do x <- line
168203
some newline <|> (do {lineComment; newline; return " "})
169204
return (Just x)
170205
<|> do {lineComment; newline; return Nothing}
171206
<|> do {space; newline; return Nothing}
172207

173-
174208
line :: Parser Opcode
175209
line = do op <- opcode
176210
case map toUpper op of
@@ -190,16 +224,54 @@ line = do op <- opcode
190224
"JEQ" -> jeq
191225
"JNE" -> jne
192226
"DS" -> ds
193-
op -> error "hello"
227+
op -> error $ "unknwn opcode `"++ op ++ "`"
228+
229+
toBin :: (Show a, Integral a) => Int -> a -> String
230+
toBin p 0 = replicate p '0'
231+
toBin p n = let xs = concatMap show $ reverse (helper n)
232+
in replicate (p - length xs) '0' ++ xs
233+
where
234+
helper 0 = []
235+
helper n = let (q,r) = n `divMod` 2 in r : helper q
236+
237+
assemble :: [Opcode] -> String
238+
assemble = unlines . (\xs -> xs ++ replicate (256 - length xs) (zeros 12)) . map helper
239+
where
240+
zeros n = replicate n '0'
241+
helper x = case x of
242+
NOOP -> "0000" ++ zeros 8
243+
(ADD r (Addr a)) -> "0001" ++ toBin 8 a
244+
(SUB r (Addr a)) -> "0010" ++ toBin 8 a
245+
(NOT r) -> "0011" ++ zeros 8
246+
(AND r (Addr a)) -> "0100" ++ toBin 8 a
247+
(CMP r (Addr a)) -> "0101" ++ toBin 8 a
248+
(LB r (Addr a)) -> "0110" ++ toBin 8 a
249+
(LBI r (AddrAddr a)) -> "0111" ++ toBin 8 a
250+
(SB (Addr a) r) -> "1000" ++ toBin 8 a
251+
(SBI (AddrAddr a) r) -> "1001" ++ toBin 8 a
252+
(IN (Addr a) b) -> "1010" ++ toBin 8 a
253+
(JA (Addr a)) -> "1011" ++ toBin 8 a
254+
(J (Offset o)) -> "1100" ++ toBin 8 o
255+
(JEQ (Offset o)) -> "1101" ++ toBin 8 o
256+
(JNE (Offset o)) -> "1110" ++ toBin 8 o
257+
DS -> "1111" ++ zeros 8
194258

259+
main :: IO ()
195260
main = do
196261
args <- getArgs
197-
when (null args) (die "No input file provided.")
262+
when (null args) (die "No input file provided. Use `-h` to see help page.")
263+
when (head args `elem` ["-h", "--help"]) (do {putStr help; exitSuccess})
198264

199265
contents <- readFile (head args)
200-
--let line = head (lines contents)
201-
print contents
202-
print . catMaybes . fst . head $ parse (many lineWithComment) contents
203-
print $ parse (many lineWithComment) contents
266+
let [(res, unparsed)] = parse (many lineWithComment) contents
267+
line = head (lines unparsed)
268+
lineNumber = (+1) . fromJust $ elemIndex line (lines contents)
269+
unless (null unparsed) (die $ "Invalid syntax at line "
270+
++ show lineNumber ++": \n"
271+
++ line ++ "\n\n"
272+
++ "Use `-h` to see help page.")
204273

274+
let parsed = catMaybes res
275+
writeFile "memory.mif" (assemble parsed)
276+
putStrLn "Wrote to memory.mif"
205277

Parser.hs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,12 @@ nat :: Parser Int
126126
nat = do x <- some digitChar
127127
return (read x)
128128

129+
int :: Parser Int
130+
int = do char '-'
131+
n <- nat
132+
return (-n)
133+
<|> nat
134+
129135
-- | Parse a string of spaces, tabs, and newlines
130136
space :: Parser String
131137
space = many (sat (\c -> c == '\t' || isSeparator c))

test.txt

Lines changed: 0 additions & 7 deletions
This file was deleted.

0 commit comments

Comments
 (0)