@@ -10,24 +10,30 @@ import Data.Char
1010import Data.Word
1111import 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
3238data Reg = ACC deriving Show
3339data IOBus = IOBus deriving Show
@@ -42,7 +48,7 @@ instance Show AddrAddr where
4248 show (AddrAddr x) = " M[M[" ++ show x++ " ]]"
4349
4450instance Show Offset where
45- show (Offset x) = " ± " ++ show x
51+ show (Offset x) = " + " ++ show x
4652
4753data Opcode = NOOP
4854 | ADD Reg Addr
@@ -83,8 +89,9 @@ opcodes = [
8389 ]
8490
8591upperLowerToken :: 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
117137addr :: 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
128147addraddr :: 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
131162offset :: Parser Offset
132- offset = Offset . fromIntegral <$> nat
163+ offset = Offset . fromIntegral <$> do { n <- int;
164+ guard (isWord8 n);
165+ return n }
133166
134167ioBus :: Parser IOBus
135168ioBus = 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
140173newline :: Parser Char
141174newline = do { space; char ' \n ' }
175+ <|> do { space; string " \r\n " ; return ' \n ' }
142176
143177noop , add , sub , nott , andd , cmp , lb , lbi , sb , sbi , inn , ja , j , jeq , jne , ds :: Parser Opcode
144178noop = 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 }
147181nott = 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
157191jeq = JEQ <$> offset
158192jne = JNE <$> offset
159193ds = return DS
@@ -164,13 +198,13 @@ lineComment = do space
164198 xs <- many (sat (/= ' \n ' ))
165199 return (c++ xs)
166200
201+ lineWithComment :: Parser (Maybe Opcode )
167202lineWithComment = 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-
174208line :: Parser Opcode
175209line = 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 ()
195260main = 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
0 commit comments