asm_compile.g 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. # This file is part of asmc, a bootstrapping OS with minimal seed
  2. # Copyright (C) 2018 Giovanni Mascellani <gio@debian.org>
  3. # https://gitlab.com/giomasce/asmc
  4. # This program is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or
  7. # (at your option) any later version.
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. # You should have received a copy of the GNU General Public License
  13. # along with this program. If not, see <https://www.gnu.org/licenses/>.
  14. fun asmctx_parse_number 2 {
  15. $ctx
  16. $str
  17. @ctx 1 param = ;
  18. @str 0 param = ;
  19. $len
  20. @len str strlen = ;
  21. len 0 > "asmctx_parse_number: invalid zero-length string" assert_msg ;
  22. $value
  23. if str **c '\'' == {
  24. @value 0 = ;
  25. str len + 1 - **c '\'' == "asmctx_parse_number: invalid immediate character" assert_msg ;
  26. str len + 1 - 0 =c ;
  27. @str str 1 + = ;
  28. $i
  29. @i 0 = ;
  30. while str i + **c 0 != {
  31. @value value 8 << = ;
  32. @value value str i + **c + = ;
  33. @i i 1 + = ;
  34. }
  35. value ret ;
  36. }
  37. $endptr
  38. @value str @endptr 0 strtol = ;
  39. if endptr **c 0 == {
  40. value ret ;
  41. }
  42. if str len + 1 - **c 'b' == str len + 1 - **c 'B' == || {
  43. $stolen
  44. @stolen str len + 1 - **c = ;
  45. str len + 1 - 0 =c ;
  46. @value str @endptr 2 strtol = ;
  47. if endptr **c 0 == {
  48. value ret ;
  49. }
  50. str len + 1 - stolen =c ;
  51. }
  52. if str len + 1 - **c 'h' == str len + 1 - **c 'H' == || {
  53. $stolen
  54. @stolen str len + 1 - **c = ;
  55. str len + 1 - 0 =c ;
  56. @value str @endptr 16 strtol = ;
  57. if endptr **c 0 == {
  58. value ret ;
  59. }
  60. str len + 1 - stolen =c ;
  61. }
  62. @value ctx str asmctx_get_symbol = ;
  63. value ret ;
  64. }
  65. fun asmctx_parse_operand 1 {
  66. $ctx
  67. @ctx 0 param = ;
  68. $op
  69. @op SIZEOF_OPERAND malloc = ;
  70. $tok
  71. @tok ctx asmctx_get_token = ;
  72. # Find the size prefix
  73. op OPERAND_SIZE take_addr 0 = ;
  74. if tok "byte" strcmp_case 0 == {
  75. op OPERAND_SIZE take_addr 1 = ;
  76. } else {
  77. if tok "word" strcmp_case 0 == {
  78. op OPERAND_SIZE take_addr 2 = ;
  79. } else {
  80. if tok "dword" strcmp_case 0 == {
  81. op OPERAND_SIZE take_addr 3 = ;
  82. }
  83. }
  84. }
  85. if op OPERAND_SIZE take 0 != {
  86. tok free ;
  87. @tok ctx asmctx_get_token = ;
  88. }
  89. # Is it direct or indirect?
  90. if tok "[" strcmp 0 == {
  91. op OPERAND_TYPE take_addr 0 = ;
  92. op OPERAND_OFFSET take_addr 0 = ;
  93. op OPERAND_REG take_addr 8 = ;
  94. op OPERAND_INDEX_REG take_addr 8 = ;
  95. op OPERAND_SEGMENT take_addr 0 = ;
  96. $cont
  97. @cont 1 = ;
  98. tok free ;
  99. @tok ctx asmctx_get_token = ;
  100. $sign
  101. @sign 1 = ;
  102. while cont {
  103. $reg
  104. @reg tok parse_register = ;
  105. if reg 0xffffffff != {
  106. sign 1 == "asmctx_parse_operand: illegal sign usage in indirect operand" assert_msg ;
  107. reg 4 >> 3 == "asmctx_parse_operand: indirect operands must use 32 bits registers" assert_msg ;
  108. @reg reg 0xf & = ;
  109. tok free ;
  110. @tok ctx asmctx_get_token = ;
  111. if tok "*" strcmp 0 == {
  112. tok free ;
  113. @tok ctx asmctx_get_token = ;
  114. $scale
  115. $endptr
  116. @scale tok @endptr 0 strtol = ;
  117. endptr **c 0 == "asmctx_parse_operand: illegal scale" assert_msg ;
  118. $scalebits
  119. if scale 1 == {
  120. @scalebits 0 = ;
  121. } else {
  122. if scale 2 == {
  123. @scalebits 1 = ;
  124. } else {
  125. if scale 4 == {
  126. @scalebits 2 = ;
  127. } else {
  128. if scale 8 == {
  129. @scalebits 3 = ;
  130. } else {
  131. 0 "asmctx_parse_operand: illegal scale" assert_msg ;
  132. }
  133. }
  134. }
  135. }
  136. op OPERAND_INDEX_REG take_addr reg = ;
  137. op OPERAND_SCALE take_addr scalebits = ;
  138. tok free ;
  139. } else {
  140. ctx asmctx_give_back_token ;
  141. if op OPERAND_REG take 8 == {
  142. op OPERAND_REG take_addr reg = ;
  143. } else {
  144. op OPERAND_INDEX_REG take 8 == "asmctx_parse_operand: more than two registers in indirect operand" assert_msg ;
  145. op OPERAND_INDEX_REG take_addr reg = ;
  146. op OPERAND_SCALE take_addr 0 = ;
  147. }
  148. }
  149. } else {
  150. $value
  151. @value ctx tok asmctx_parse_number = ;
  152. op OPERAND_OFFSET take_addr op OPERAND_OFFSET take value sign * + = ;
  153. tok free ;
  154. }
  155. @tok ctx asmctx_get_token = ;
  156. if tok "]" strcmp 0 == {
  157. tok free ;
  158. @cont 0 = ;
  159. } else {
  160. if tok "+" strcmp 0 == {
  161. tok free ;
  162. @tok ctx asmctx_get_token = ;
  163. @sign 1 = ;
  164. } else {
  165. if tok "-" strcmp 0 == {
  166. tok free ;
  167. @tok ctx asmctx_get_token = ;
  168. @sign 0 1 - = ;
  169. } else {
  170. 0 "asmctx_parse_operand: illegal character while scanning an indirect operand" assert_msg ;
  171. }
  172. }
  173. }
  174. }
  175. } else {
  176. op OPERAND_SIZE take 0 == "Cannot specify the size of a direct operand" assert_msg ;
  177. $cont
  178. @cont 1 = ;
  179. $value
  180. @value 0 = ;
  181. op OPERAND_TYPE take_addr 0 = ;
  182. $sign
  183. @sign 0 = ;
  184. while cont {
  185. $reg
  186. @reg tok parse_register = ;
  187. if reg 0xffffffff != {
  188. op OPERAND_TYPE take 0 == "asmctx_parse_operand: invalid direct operand" assert_msg ;
  189. op OPERAND_TYPE take_addr 1 = ;
  190. op OPERAND_REG take_addr reg 0x0f & = ;
  191. op OPERAND_SIZE take_addr reg 4 >> = ;
  192. @cont 0 = ;
  193. tok free ;
  194. } else {
  195. if tok "+" strcmp 0 == tok "-" strcmp 0 == || tok "not" strcmp 0 == || ! {
  196. op OPERAND_TYPE take 0 == op OPERAND_TYPE take 2 == || "asmctx_parse_operand: invalid direct operand" assert_msg ;
  197. op OPERAND_TYPE take_addr 2 = ;
  198. $opvalue
  199. @opvalue ctx tok asmctx_parse_number = ;
  200. tok free ;
  201. @tok ctx asmctx_get_token = ;
  202. if tok "shl" strcmp 0 == {
  203. tok free ;
  204. @tok ctx asmctx_get_token = ;
  205. $shift
  206. @shift ctx tok asmctx_parse_number = ;
  207. @opvalue opvalue shift << = ;
  208. tok free ;
  209. @tok ctx asmctx_get_token = ;
  210. } else {
  211. if tok "shr" strcmp 0 == {
  212. tok free ;
  213. @tok ctx asmctx_get_token = ;
  214. $shift
  215. @shift ctx tok asmctx_parse_number = ;
  216. @opvalue opvalue shift << = ;
  217. tok free ;
  218. @tok ctx asmctx_get_token = ;
  219. } else {
  220. if tok "or" strcmp 0 == {
  221. tok free ;
  222. @tok ctx asmctx_get_token = ;
  223. $op2
  224. @op2 ctx tok asmctx_parse_number = ;
  225. @opvalue opvalue op2 | = ;
  226. tok free ;
  227. @tok ctx asmctx_get_token = ;
  228. } else {
  229. if tok "*" strcmp 0 == {
  230. tok free ;
  231. @tok ctx asmctx_get_token = ;
  232. $op2
  233. @op2 ctx tok asmctx_parse_number = ;
  234. @opvalue opvalue op2 * = ;
  235. tok free ;
  236. @tok ctx asmctx_get_token = ;
  237. }
  238. }
  239. }
  240. }
  241. if sign 0 == {
  242. @value value opvalue + = ;
  243. } else {
  244. if sign 1 == {
  245. @value value opvalue - = ;
  246. } else {
  247. if sign 2 == {
  248. @value value opvalue - 1 - = ;
  249. } else {
  250. 0 "asmctx_parse_operand: error 1" assert_msg ;
  251. }
  252. }
  253. }
  254. }
  255. if tok "+" strcmp 0 == {
  256. tok free ;
  257. @tok ctx asmctx_get_token = ;
  258. @sign 0 = ;
  259. } else {
  260. if tok "-" strcmp 0 == {
  261. tok free ;
  262. @tok ctx asmctx_get_token = ;
  263. @sign 1 = ;
  264. } else {
  265. if tok "not" strcmp 0 == {
  266. tok free ;
  267. @tok ctx asmctx_get_token = ;
  268. @sign 2 = ;
  269. } else {
  270. ctx asmctx_give_back_token ;
  271. @cont 0 = ;
  272. }
  273. }
  274. }
  275. }
  276. }
  277. if op OPERAND_TYPE take 2 == {
  278. op OPERAND_OFFSET take_addr value = ;
  279. }
  280. }
  281. op ret ;
  282. }
  283. fun asmctx_parse_db 1 {
  284. $ctx
  285. @ctx 0 param = ;
  286. while 1 {
  287. $tok
  288. @tok ctx asmctx_get_token = ;
  289. if tok **c '\'' == {
  290. ctx tok emit_string ;
  291. tok free ;
  292. } else {
  293. if tok "?" strcmp 0 == {
  294. tok free ;
  295. ctx 0 asmctx_emit ;
  296. } else {
  297. ctx asmctx_give_back_token ;
  298. $op
  299. @op ctx asmctx_parse_operand = ;
  300. op OPERAND_TYPE take 2 == "asmctx_parse_db: immediate operand expected" assert_msg ;
  301. $value
  302. @value op OPERAND_OFFSET take = ;
  303. op free ;
  304. ctx value asmctx_emit ;
  305. }
  306. }
  307. @tok ctx asmctx_get_token = ;
  308. if tok "\n" strcmp 0 == {
  309. ctx asmctx_give_back_token ;
  310. ret ;
  311. }
  312. tok "," strcmp 0 == "asmctx_parse_db: comma expected" assert_msg ;
  313. tok free ;
  314. }
  315. }
  316. fun asmctx_parse_data 1 {
  317. $ctx
  318. @ctx 0 param = ;
  319. $type
  320. $size
  321. $arg
  322. @type 0 = ;
  323. @size 0 = ;
  324. @arg 0 = ;
  325. $tok
  326. @tok ctx asmctx_get_token = ;
  327. if tok "align" strcmp 0 == {
  328. @type 3 = ;
  329. @size 1 = ;
  330. } else {
  331. if tok strlen 2 != type 0 == && {
  332. ctx asmctx_give_back_token ;
  333. 0 ret ;
  334. }
  335. if tok "db" strcmp 0 == {
  336. tok free ;
  337. ctx asmctx_parse_db ;
  338. 1 ret ;
  339. }
  340. if tok **c 'd' == {
  341. @type 1 = ;
  342. }
  343. if tok **c 'r' == {
  344. @type 2 = ;
  345. }
  346. if tok 1 + **c 'b' == {
  347. @size 1 = ;
  348. }
  349. if tok 1 + **c 'w' == {
  350. @size 2 = ;
  351. }
  352. if tok 1 + **c 'd' == {
  353. @size 3 = ;
  354. }
  355. if tok 1 + **c 'q' == {
  356. @size 4 = ;
  357. }
  358. }
  359. if type 0 == size 0 == || {
  360. ctx asmctx_give_back_token ;
  361. 0 ret ;
  362. }
  363. tok free ;
  364. if type 1 == {
  365. @tok ctx asmctx_get_token = ;
  366. $value
  367. if tok "?" strcmp 0 == {
  368. tok free ;
  369. ctx 0 size emit_size ;
  370. } else {
  371. ctx asmctx_give_back_token ;
  372. $cont
  373. @cont 1 = ;
  374. while cont {
  375. $op
  376. @op ctx asmctx_parse_operand = ;
  377. op OPERAND_TYPE take 2 == "asmctx_parse_data: immediate operand expected" assert_msg ;
  378. $value
  379. @value op OPERAND_OFFSET take = ;
  380. op free ;
  381. ctx value size emit_size ;
  382. @tok ctx asmctx_get_token = ;
  383. if tok "," strcmp 0 == {
  384. tok free ;
  385. } else {
  386. @cont 0 = ;
  387. ctx asmctx_give_back_token ;
  388. }
  389. }
  390. }
  391. } else {
  392. $op
  393. @op ctx asmctx_parse_operand = ;
  394. op OPERAND_TYPE take 2 == "asmctx_parse_data: immediate operand expected" assert_msg ;
  395. $reps
  396. if type 2 == {
  397. @reps op OPERAND_OFFSET take = ;
  398. } else {
  399. $align
  400. @align op OPERAND_OFFSET take = ;
  401. $rem
  402. @rem ctx ASMCTX_CURRENT_LOC take align % = ;
  403. if rem 0 == {
  404. @reps 0 = ;
  405. } else {
  406. @reps align rem - = ;
  407. }
  408. }
  409. op free ;
  410. while reps 0 > {
  411. ctx 0 size emit_size ;
  412. @reps reps 1 - = ;
  413. }
  414. }
  415. 1 ret ;
  416. }
  417. fun asmctx_parse_operands 1 {
  418. $ctx
  419. @ctx 0 param = ;
  420. $ops
  421. @ops 4 vector_init = ;
  422. $cont
  423. @cont 1 = ;
  424. while cont {
  425. $tok
  426. @tok ctx asmctx_get_token = ;
  427. if tok "\n" strcmp 0 == {
  428. @cont 0 = ;
  429. ctx asmctx_give_back_token ;
  430. } else {
  431. ctx asmctx_give_back_token ;
  432. $op
  433. @op ctx asmctx_parse_operand = ;
  434. ops op vector_push_back ;
  435. @tok ctx asmctx_get_token = ;
  436. if tok "," strcmp 0 != {
  437. @cont 0 = ;
  438. ctx asmctx_give_back_token ;
  439. } else {
  440. tok free ;
  441. }
  442. }
  443. }
  444. ops ret ;
  445. }
  446. fun asmctx_parse_line 2 {
  447. $ctx
  448. $opcode_map
  449. @ctx 1 param = ;
  450. @opcode_map 0 param = ;
  451. $tok
  452. @tok ctx asmctx_get_token = ;
  453. if tok "\n" strcmp 0 == {
  454. tok free ;
  455. 1 ret ;
  456. }
  457. if tok "" strcmp 0 == {
  458. tok free ;
  459. 0 ret ;
  460. }
  461. if tok "rep" strcmp 0 == tok "repe" strcmp 0 == || {
  462. tok free ;
  463. @tok ctx asmctx_get_token = ;
  464. ctx 0xf3 asmctx_emit ;
  465. }
  466. if opcode_map tok map_has {
  467. $opcode
  468. @opcode opcode_map tok map_at = ;
  469. tok free ;
  470. $i
  471. @i 0 = ;
  472. $arg_num
  473. @arg_num opcode OPCODE_ARG_NUM take 0xff & = ;
  474. $can_repeat
  475. @can_repeat opcode OPCODE_ARG_NUM take 8 >> = ;
  476. $cont
  477. @cont 1 = ;
  478. while cont {
  479. $ops
  480. @ops ctx asmctx_parse_operands = ;
  481. arg_num 0xff == arg_num ops vector_size == || "asmctx_parse_line: wrong number of operands" assert_msg ;
  482. ctx opcode ops opcode OPCODE_HANDLER take \3 ;
  483. ops free_vect_of_ptrs ;
  484. if can_repeat ! {
  485. @cont 0 = ;
  486. } else {
  487. @tok ctx asmctx_get_token = ;
  488. if tok "\n" strcmp 0 == {
  489. @cont 0 = ;
  490. }
  491. ctx asmctx_give_back_token ;
  492. }
  493. }
  494. } else {
  495. ctx asmctx_give_back_token ;
  496. if ctx asmctx_parse_data ! {
  497. $label
  498. @label ctx asmctx_get_token = ;
  499. @tok ctx asmctx_get_token = ;
  500. if tok ":" strcmp 0 == {
  501. tok free ;
  502. ctx label ctx ASMCTX_CURRENT_LOC take asmctx_add_symbol ;
  503. } else {
  504. ctx label ctx ASMCTX_CURRENT_LOC take asmctx_add_symbol ;
  505. ctx asmctx_give_back_token ;
  506. if ctx asmctx_parse_data ! {
  507. 0 "asmctx_parse_line: unknown command" assert_msg ;
  508. }
  509. }
  510. label free ;
  511. }
  512. }
  513. @tok ctx asmctx_get_token = ;
  514. tok "\n" strcmp 0 == "parse_asm_line: expected line terminator" assert_msg ;
  515. tok free ;
  516. 1 ret ;
  517. }
  518. fun asmctx_compile 1 {
  519. $ctx
  520. @ctx 0 param = ;
  521. ctx ASMCTX_STAGE take_addr 0 = ;
  522. $start_loc
  523. @start_loc 0 = ;
  524. $size
  525. $opcode_map
  526. @opcode_map build_opcode_map = ;
  527. while ctx ASMCTX_STAGE take 3 < {
  528. if ctx ASMCTX_VERBOSE take {
  529. "Compilation stage " log ;
  530. ctx ASMCTX_STAGE take 1 + itoa log ;
  531. }
  532. $line_num
  533. @line_num 1 = ;
  534. ctx ASMCTX_FDIN take vfs_reset ;
  535. ctx ASMCTX_CURRENT_LOC take_addr start_loc = ;
  536. ctx ASMCTX_TOKEN_GIVEN_BACK take_addr 0 = ;
  537. ctx ASMCTX_CHAR_GIVEN_BACK take_addr 0 = ;
  538. $cont
  539. @cont 1 = ;
  540. while cont {
  541. if line_num 1000 % 0 == ctx ASMCTX_VERBOSE take && {
  542. "." log ;
  543. }
  544. @cont ctx opcode_map asmctx_parse_line = ;
  545. @line_num line_num 1 + = ;
  546. }
  547. if ctx ASMCTX_VERBOSE take {
  548. "\n" log ;
  549. }
  550. if ctx ASMCTX_STAGE take 0 == {
  551. @size ctx ASMCTX_CURRENT_LOC take start_loc - = ;
  552. @start_loc size platform_allocate = ;
  553. } else {
  554. ctx ASMCTX_CURRENT_LOC take start_loc - size == "asmctx_compile: error 1" assert_msg ;
  555. }
  556. ctx ASMCTX_STAGE take_addr ctx ASMCTX_STAGE take 1 + = ;
  557. }
  558. if ctx ASMCTX_VERBOSE take {
  559. "Assembled program has size " log ;
  560. size itoa log ;
  561. " and starts at " log ;
  562. start_loc itoa log ;
  563. "\n" log ;
  564. }
  565. if ctx ASMCTX_DEBUG take {
  566. "Compiled dump:\n" log ;
  567. start_loc size dump_mem ;
  568. "\n" log ;
  569. }
  570. opcode_map destroy_opcode_map ;
  571. }
  572. fun parse_asm 1 {
  573. $filename
  574. @filename 0 param = ;
  575. $ctx
  576. @ctx asmctx_init = ;
  577. $cont
  578. @cont 1 = ;
  579. $fd
  580. @fd filename vfs_open = ;
  581. ctx fd asmctx_set_fd ;
  582. # while cont {
  583. # $tok
  584. # @tok ctx asmctx_get_token = ;
  585. # @cont tok **c 0 != = ;
  586. # if tok **c '\n' == {
  587. # "NL" log ;
  588. # } else {
  589. # tok log ;
  590. # }
  591. # "#" log ;
  592. # tok free ;
  593. # }
  594. # "\n" log ;
  595. ctx asmctx_compile ;
  596. fd vfs_close ;
  597. ctx asmctx_destroy ;
  598. }