Mirror of CollapseOS
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1153 lines
30KB

  1. ; *** Consts ***
  2. ; Number of rows in the argspec table
  3. .equ ARGSPEC_TBL_CNT 31
  4. ; Number of rows in the primary instructions table
  5. .equ INSTR_TBL_CNT 151
  6. ; size in bytes of each row in the primary instructions table
  7. .equ INSTR_TBL_ROWSIZE 6
  8. ; Instruction IDs They correspond to the index of the table in instrNames
  9. .equ I_ADC 0x00
  10. .equ I_ADD 0x01
  11. .equ I_AND 0x02
  12. .equ I_BIT 0x03
  13. .equ I_CALL 0x04
  14. .equ I_CCF 0x05
  15. .equ I_CP 0x06
  16. .equ I_CPD 0x07
  17. .equ I_CPDR 0x08
  18. .equ I_CPI 0x09
  19. .equ I_CPIR 0x0a
  20. .equ I_CPL 0x0b
  21. .equ I_DAA 0x0c
  22. .equ I_DEC 0x0d
  23. .equ I_DI 0x0e
  24. .equ I_DJNZ 0x0f
  25. .equ I_EI 0x10
  26. .equ I_EX 0x11
  27. .equ I_EXX 0x12
  28. .equ I_HALT 0x13
  29. .equ I_IM 0x14
  30. .equ I_IN 0x15
  31. .equ I_INC 0x16
  32. .equ I_IND 0x17
  33. .equ I_INDR 0x18
  34. .equ I_INI 0x19
  35. .equ I_INIR 0x1a
  36. .equ I_JP 0x1b
  37. .equ I_JR 0x1c
  38. .equ I_LD 0x1d
  39. .equ I_LDD 0x1e
  40. .equ I_LDDR 0x1f
  41. .equ I_LDI 0x20
  42. .equ I_LDIR 0x21
  43. .equ I_NEG 0x22
  44. .equ I_NOP 0x23
  45. .equ I_OR 0x24
  46. .equ I_OTDR 0x25
  47. .equ I_OTIR 0x26
  48. .equ I_OUT 0x27
  49. .equ I_POP 0x28
  50. .equ I_PUSH 0x29
  51. .equ I_RET 0x2a
  52. .equ I_RETI 0x2b
  53. .equ I_RETN 0x2c
  54. .equ I_RL 0x2d
  55. .equ I_RLA 0x2e
  56. .equ I_RLC 0x2f
  57. .equ I_RLCA 0x30
  58. .equ I_RR 0x31
  59. .equ I_RRA 0x32
  60. .equ I_RRC 0x33
  61. .equ I_RRCA 0x34
  62. .equ I_SBC 0x35
  63. .equ I_SCF 0x36
  64. .equ I_SUB 0x37
  65. .equ I_XOR 0x38
  66. ; Checks whether A is 'N' or 'M'
  67. checkNOrM:
  68. cp 'N'
  69. ret z
  70. cp 'M'
  71. ret
  72. ; Checks whether A is 'n', 'm', 'x' or 'y'
  73. checknmxy:
  74. cp 'n'
  75. ret z
  76. cp 'm'
  77. ret z
  78. cp 'x'
  79. ret z
  80. cp 'y'
  81. ret
  82. ; Reads string in (HL) and returns the corresponding ID (I_*) in A. Sets Z if
  83. ; there's a match.
  84. getInstID:
  85. push bc
  86. push de
  87. ld b, I_XOR+1 ; I_XOR is the last
  88. ld c, 4
  89. ld de, instrNames
  90. call findStringInList
  91. pop de
  92. pop bc
  93. ret
  94. ; Parse the string at (HL) and check if it starts with IX+, IY+, IX- or IY-.
  95. ; Sets Z if yes, unset if no.
  96. parseIXY:
  97. push hl
  98. ld a, (hl)
  99. call upcase
  100. cp 'I'
  101. jr nz, .end ; Z already unset
  102. inc hl
  103. ld a, (hl)
  104. call upcase
  105. cp 'X'
  106. jr z, .match1
  107. cp 'Y'
  108. jr z, .match1
  109. jr .end ; Z already unset
  110. .match1:
  111. ; Alright, we have IX or IY. Let's see if we have + or - next.
  112. inc hl
  113. ld a, (hl)
  114. cp '+'
  115. jr z, .end ; Z is already set
  116. cp '-'
  117. ; The value of Z at this point is our final result
  118. .end:
  119. pop hl
  120. ret
  121. ; find argspec for string at (HL). Returns matching argspec in A.
  122. ; Return value 0xff holds a special meaning: arg is not empty, but doesn't match
  123. ; any argspec (A == 0 means arg is empty). A return value of 0xff means an
  124. ; error.
  125. ;
  126. ; If the parsed argument is a number constant, 'N' is returned and IX contains
  127. ; the value of that constant.
  128. parseArg:
  129. call strlen
  130. or a
  131. ret z ; empty string? A already has our result: 0
  132. push bc
  133. push de
  134. push hl
  135. ; We always initialize IX to zero so that non-numerical args end up with
  136. ; a clean zero.
  137. ld ix, 0
  138. ld de, argspecTbl
  139. ; DE now points the the "argspec char" part of the entry, but what
  140. ; we're comparing in the loop is the string next to it. Let's offset
  141. ; DE by one so that the loop goes through strings.
  142. inc de
  143. ld b, ARGSPEC_TBL_CNT
  144. .loop1:
  145. ld a, 4
  146. call strncmpI
  147. jr z, .found ; got it!
  148. ld a, 5
  149. call addDE
  150. djnz .loop1
  151. ; We exhausted the argspecs. Let's see if we're inside parens.
  152. call enterParens
  153. jr z, .withParens
  154. ; (HL) has no parens
  155. call .maybeParseExpr
  156. jr nz, .nomatch
  157. ; We have a proper number in no parens. Number in IX.
  158. ld a, 'N'
  159. jr .end
  160. .withParens:
  161. ld c, 'M' ; C holds the argspec type until we reach
  162. ; .numberInParens
  163. ; We have parens. First, let's see if we have a (IX+d) type of arg.
  164. call parseIXY
  165. jr nz, .parseNumberInParens ; not I{X,Y}. just parse number.
  166. ; We have IX+/IY+/IX-/IY-.
  167. ; note: the "-" part isn't supported yet.
  168. inc hl ; (HL) now points to X or Y
  169. ld a, (hl)
  170. inc hl ; advance HL to the number part
  171. inc hl ; this is the number
  172. cp 'Y'
  173. jr nz, .notY
  174. ld c, 'y'
  175. jr .parseNumberInParens
  176. .notY:
  177. ld c, 'x'
  178. .parseNumberInParens:
  179. call .maybeParseExpr
  180. jr nz, .nomatch
  181. ; We have a proper number in parens. Number in IX
  182. ld a, c ; M, x, or y
  183. jr .end
  184. .nomatch:
  185. ; We get no match
  186. ld a, 0xff
  187. jr .end
  188. .found:
  189. ; found the matching argspec row. Our result is one byte left of DE.
  190. dec de
  191. ld a, (de)
  192. .end:
  193. pop hl
  194. pop de
  195. pop bc
  196. ret
  197. .maybeParseExpr:
  198. ; Before we try to parse expr in (HL), first check if we're in first
  199. ; pass if we are, skip parseExpr. Most of the time, that parse is
  200. ; harmless, but in some cases it causes false failures. For example,
  201. ; a "-" operator can cause is to falsely overflow and generate
  202. ; truncation error.
  203. call zasmIsFirstPass
  204. ret z
  205. jp parseExpr
  206. ; Returns, with Z, whether A is a groupId
  207. isGroupId:
  208. cp 0xc ; max group id + 1
  209. jr nc, .notgroup ; >= 0xc? not a group
  210. cp 0
  211. jr z, .notgroup ; 0? not supposed to happen. something's wrong.
  212. ; A is a group. ensure Z is set
  213. cp a
  214. ret
  215. .notgroup:
  216. call unsetZ
  217. ret
  218. ; Find argspec A in group id H.
  219. ; Set Z according to whether we found the argspec
  220. ; If found, the value in A is the argspec value in the group (its index).
  221. findInGroup:
  222. push bc
  223. push hl
  224. or a ; is our arg empty? If yes, we have nothing to do
  225. jr z, .notfound
  226. push af
  227. ld a, h
  228. cp 0xa
  229. jr z, .specialGroupCC
  230. cp 0xb
  231. jr z, .specialGroupABCDEHL
  232. jr nc, .notfound ; > 0xb? not a group
  233. pop af
  234. ; regular group
  235. push de
  236. ld de, argGrpTbl
  237. ; group ids start at 1. decrease it, then multiply by 4 to have a
  238. ; proper offset in argGrpTbl
  239. dec h
  240. push af
  241. ld a, h
  242. rla
  243. rla
  244. call addDE ; At this point, DE points to our group
  245. pop af
  246. ex de, hl ; And now, HL points to the group
  247. pop de
  248. ld bc, 4
  249. jr .find
  250. .specialGroupCC:
  251. ld hl, argGrpCC
  252. jr .specialGroupEnd
  253. .specialGroupABCDEHL:
  254. ld hl, argGrpABCDEHL
  255. .specialGroupEnd:
  256. pop af ; from the push af just before the special group check
  257. ld bc, 8
  258. .find:
  259. ; This part is common to regular and special group. We expect HL to
  260. ; point to the group and BC to contain its length.
  261. push bc ; save the start value loop index so we can sub
  262. .loop:
  263. cpi
  264. jr z, .found
  265. jp po, .notfound
  266. jr .loop
  267. .found:
  268. ; we found our result! Now, what we want to put in A is the index of
  269. ; the found argspec.
  270. pop hl ; we pop from the "push bc" above. L is now 4 or 8
  271. ld a, l
  272. sub c
  273. dec a ; cpi DECs BC even when there's a match, so C == the
  274. ; number of iterations we've made. But our index is
  275. ; zero-based (1 iteration == 0 index).
  276. cp a ; ensure Z is set
  277. jr .end
  278. .notfound:
  279. pop bc ; from the push bc in .find
  280. call unsetZ
  281. .end:
  282. pop hl
  283. pop bc
  284. ret
  285. ; Compare argspec from instruction table in A with argument in (HL).
  286. ; For constant args, it's easy: if A == (HL), it's a success.
  287. ; If it's not this, then we check if it's a numerical arg.
  288. ; If A is a group ID, we do something else: we check that (HL) exists in the
  289. ; groupspec (argGrpTbl). Moreover, we go and write the group's "value" (index)
  290. ; in (HL+1). This will save us significant processing later in getUpcode.
  291. ; Set Z according to whether we match or not.
  292. matchArg:
  293. cp (hl)
  294. ret z
  295. ; not an exact match. Before we continue: is A zero? Because if it is,
  296. ; we have to stop right here: no match possible.
  297. or a
  298. jr nz, .checkIfNumber ; not a zero, we can continue
  299. ; zero, stop here
  300. call unsetZ
  301. ret
  302. .checkIfNumber:
  303. ; not an exact match, let's check for numerical constants.
  304. call upcase
  305. call checkNOrM
  306. jr z, .expectsNumber
  307. jr .notNumber
  308. .expectsNumber:
  309. ; Our argument is a number N or M. Never a lower-case version. At this
  310. ; point in the processing, we don't care about whether N or M is upper,
  311. ; we do truncation tests later. So, let's just perform the same == test
  312. ; but in a case-insensitive way instead
  313. cp (hl)
  314. ret ; whether we match or not, the result of Z is
  315. ; the good one.
  316. .notNumber:
  317. ; A bit of a delicate situation here: we want A to go in H but also
  318. ; (HL) to go in A. If not careful, we overwrite each other. EXX is
  319. ; necessary to avoid invoving other registers.
  320. push hl
  321. exx
  322. ld h, a
  323. push hl
  324. exx
  325. ld a, (hl)
  326. pop hl
  327. call findInGroup
  328. pop hl
  329. ret nz
  330. ; we found our group? let's write down its "value" in (HL+1). We hold
  331. ; this value in A at the moment.
  332. inc hl
  333. ld (hl), a
  334. dec hl
  335. ret
  336. ; Compare primary row at (DE) with ID in A. Sets Z flag if there's a match.
  337. matchPrimaryRow:
  338. push hl
  339. push ix
  340. push de \ pop ix
  341. cp (ix)
  342. jr nz, .end
  343. ; name matches, let's see the rest
  344. ld hl, curArg1
  345. ld a, (ix+1)
  346. call matchArg
  347. jr nz, .end
  348. ld hl, curArg2
  349. ld a, (ix+2)
  350. call matchArg
  351. .end:
  352. pop ix
  353. pop hl
  354. ret
  355. ; *** Special opcodes ***
  356. ; The special upcode handling routines below all have the same signature.
  357. ; Instruction row is at IX and we're expected to perform the same task as
  358. ; getUpcode. The number of bytes, however, must go in C instead of A
  359. ; No need to preserve HL, DE, BC and IX: it's handled by getUpcode already.
  360. ; Handle like a regular "JP (IX+d)" except that we refuse any displacement: if
  361. ; a displacement is specified, we error out.
  362. handleJPIX:
  363. ld a, 0xdd
  364. jr handleJPIXY
  365. handleJPIY:
  366. ld a, 0xfd
  367. handleJPIXY:
  368. ld (instrUpcode), a
  369. ld a, (curArg1+1)
  370. cp 0 ; numerical argument *must* be zero
  371. jr nz, .error
  372. ; ok, we're good
  373. ld a, 0xe9 ; second upcode
  374. ld (instrUpcode+1), a
  375. ld c, 2
  376. ret
  377. .error:
  378. ld c, 0
  379. ret
  380. ; Handle the first argument of BIT. Sets Z if first argument is valid, unset it
  381. ; if there's an error.
  382. handleBIT:
  383. ld a, (curArg1+1)
  384. cp 8
  385. jr nc, .error ; >= 8? error
  386. ; We're good
  387. cp a ; ensure Z
  388. ret
  389. .error:
  390. ld c, 0
  391. call unsetZ
  392. ret
  393. handleBITHL:
  394. call handleBIT
  395. ret nz ; error
  396. ld a, 0xcb ; first upcode
  397. ld (instrUpcode), a
  398. ld a, (curArg1+1) ; 0-7
  399. ld b, 3 ; displacement
  400. call rlaX
  401. or 0b01000110 ; 2nd upcode
  402. ld (instrUpcode+1), a
  403. ld c, 2
  404. ret
  405. handleBITIX:
  406. ld a, 0xdd
  407. jr handleBITIXY
  408. handleBITIY:
  409. ld a, 0xfd
  410. handleBITIXY:
  411. ld (instrUpcode), a ; first upcode
  412. call handleBIT
  413. ret nz ; error
  414. ld a, 0xcb ; 2nd upcode
  415. ld (instrUpcode+1), a
  416. ld a, (curArg2+1) ; IXY displacement
  417. ld (instrUpcode+2), a
  418. ld a, (curArg1+1) ; 0-7
  419. ld b, 3 ; displacement
  420. call rlaX
  421. or 0b01000110 ; 4th upcode
  422. ld (instrUpcode+3), a
  423. ld c, 4
  424. ret
  425. handleBITR:
  426. call handleBIT
  427. ret nz ; error
  428. ; get group value
  429. ld a, (curArg2+1) ; group value
  430. ld c, a
  431. ; write first upcode
  432. ld a, 0xcb ; first upcode
  433. ld (instrUpcode), a
  434. ; get bit value
  435. ld a, (curArg1+1) ; 0-7
  436. ld b, 3 ; displacement
  437. call rlaX
  438. ; Now we have group value in stack, bit value in A (properly shifted)
  439. ; and we want to OR them together
  440. or c ; Now we have our ORed value
  441. or 0b01000000 ; and with the constant value for that byte...
  442. ; we're good!
  443. ld (instrUpcode+1), a
  444. ld c, 2
  445. ret
  446. handleIM:
  447. ld a, (curArg1+1)
  448. cp 0
  449. jr z, .im0
  450. cp 1
  451. jr z, .im1
  452. cp 2
  453. jr z, .im2
  454. ; error
  455. ld c, 0
  456. ret
  457. .im0:
  458. ld a, 0x46
  459. jr .proceed
  460. .im1:
  461. ld a, 0x56
  462. jr .proceed
  463. .im2:
  464. ld a, 0x5e
  465. .proceed:
  466. ld (instrUpcode+1), a
  467. ld a, 0xed
  468. ld (instrUpcode), a
  469. ld c, 2
  470. ret
  471. handleLDIXn:
  472. ld a, 0xdd
  473. jr handleLDIXYn
  474. handleLDIYn:
  475. ld a, 0xfd
  476. handleLDIXYn:
  477. ld (instrUpcode), a
  478. ld a, 0x36 ; second upcode
  479. ld (instrUpcode+1), a
  480. ld a, (curArg1+1) ; IXY displacement
  481. ld (instrUpcode+2), a
  482. ld a, (curArg2+1) ; N
  483. ld (instrUpcode+3), a
  484. ld c, 4
  485. ret
  486. handleLDIXr:
  487. ld a, 0xdd
  488. jr handleLDIXYr
  489. handleLDIYr:
  490. ld a, 0xfd
  491. handleLDIXYr:
  492. ld (instrUpcode), a
  493. ld a, (curArg2+1) ; group value
  494. or 0b01110000 ; second upcode
  495. ld (instrUpcode+1), a
  496. ld a, (curArg1+1) ; IXY displacement
  497. ld (instrUpcode+2), a
  498. ld c, 3
  499. ret
  500. handleLDrIX:
  501. ld a, 0xdd
  502. jr handleLDrIXY
  503. handleLDrIY:
  504. ld a, 0xfd
  505. handleLDrIXY:
  506. ld (instrUpcode), a
  507. ld a, (curArg1+1) ; group value
  508. rla \ rla \ rla
  509. or 0b01000110 ; second upcode
  510. ld (instrUpcode+1), a
  511. ld a, (curArg2+1) ; IXY displacement
  512. ld (instrUpcode+2), a
  513. ld c, 3
  514. ret
  515. handleLDrr:
  516. ; first argument is displaced by 3 bits, second argument is not
  517. ; displaced and we or that with a leading 0b01000000
  518. ld a, (curArg1+1) ; group value
  519. rla
  520. rla
  521. rla
  522. ld c, a ; store it
  523. ld a, (curArg2+1) ; other group value
  524. or c
  525. or 0b01000000
  526. ld (instrUpcode), a
  527. ld c, 1
  528. ret
  529. ; Compute the upcode for argspec row at (DE) and arguments in curArg{1,2} and
  530. ; writes the resulting upcode in instrUpcode. A is the number if bytes written
  531. ; to instrUpcode (can be zero if something went wrong).
  532. getUpcode:
  533. push ix
  534. push de
  535. push hl
  536. push bc
  537. ; First, let's go in IX mode. It's easier to deal with offsets here.
  538. push de \ pop ix
  539. ; Are we a "special instruction"?
  540. bit 5, (ix+3)
  541. jr z, .normalInstr ; not set: normal instruction
  542. ; We are a special instruction. Fetch handler (little endian, remember).
  543. ld l, (ix+4)
  544. ld h, (ix+5)
  545. call callHL
  546. ; We have our result written in instrUpcode and C is set.
  547. jp .end
  548. .normalInstr:
  549. ; we begin by writing our "base upcode", which can be one or two bytes
  550. ld a, (ix+4) ; first upcode
  551. ld (instrUpcode), a
  552. ld de, instrUpcode ; from this point, DE points to "where we are"
  553. ; in terms of upcode writing.
  554. inc de ; make DE point to where we should write next.
  555. ld c, 1 ; C holds our upcode count
  556. ; Now, let's determine if we have one or two upcode. As a general rule,
  557. ; we simply have to check if (ix+5) == 0, which means one upcode.
  558. ; However, some two-upcodes instructions have a 0 (ix+5) because they
  559. ; expect group OR-ing into it and all other bits are zero. See "RLC r".
  560. ; To handle those cases, we *also* check for Bit 6 in (ix+3).
  561. ld a, (ix+5) ; second upcode
  562. or a ; do we have a second upcode?
  563. jr nz, .twoUpcodes
  564. bit 6, (ix+3)
  565. jr z, .onlyOneUpcode ; not set: single upcode
  566. .twoUpcodes:
  567. ; we have two upcodes
  568. ld (de), a
  569. inc de
  570. inc c
  571. .onlyOneUpcode:
  572. ; now, let's see if we're dealing with a group here
  573. ld a, (ix+1) ; first argspec
  574. call isGroupId
  575. jr z, .firstArgIsGroup
  576. ; First arg not a group. Maybe second is?
  577. ld a, (ix+2) ; 2nd argspec
  578. call isGroupId
  579. jr nz, .writeExtraBytes ; not a group? nothing to do. go to
  580. ; next step: write extra bytes
  581. ; Second arg is group
  582. ld hl, curArg2
  583. jr .isGroup
  584. .firstArgIsGroup:
  585. ld hl, curArg1
  586. .isGroup:
  587. ; A is a group, good, now let's get its value. HL is pointing to
  588. ; the argument. Our group value is at (HL+1).
  589. inc hl
  590. ld a, (hl)
  591. ; Now, we have our arg "group value" in A. Were going to need to
  592. ; displace it left by the number of steps specified in the table.
  593. push af
  594. ld a, (ix+3) ; displacement bit
  595. and 0xf ; we only use the lower nibble.
  596. ld b, a
  597. pop af
  598. call rlaX
  599. ; At this point, we have a properly displaced value in A. We'll want
  600. ; to OR it with the opcode.
  601. ; However, we first have to verify whether this ORing takes place on
  602. ; the second upcode or the first.
  603. bit 6, (ix+3)
  604. jr z, .firstUpcode ; not set: first upcode
  605. or (ix+5) ; second upcode
  606. ld (instrUpcode+1), a
  607. jr .writeExtraBytes
  608. .firstUpcode:
  609. or (ix+4) ; first upcode
  610. ld (instrUpcode), a
  611. jr .writeExtraBytes
  612. .writeExtraBytes:
  613. ; Good, we are probably finished here for many primary opcodes. However,
  614. ; some primary opcodes take 8 or 16 bit constants as an argument and
  615. ; if that's the case here, we need to write it too.
  616. ; We still have our instruction row in IX and we have DE pointing to
  617. ; where we should write next (which could be the second or the third
  618. ; byte of instrUpcode).
  619. ld a, (ix+1) ; first argspec
  620. ld hl, curArg1
  621. call checkNOrM
  622. jr z, .withWord
  623. call checknmxy
  624. jr z, .withByte
  625. ld a, (ix+2) ; second argspec
  626. ld hl, curArg2
  627. call checkNOrM
  628. jr z, .withWord
  629. call checknmxy
  630. jr z, .withByte
  631. ; nope, no number, alright, we're finished here
  632. jr .end
  633. .withByte:
  634. inc hl
  635. ; HL points to our number (LSB), with (HL+1) being our MSB which should
  636. ; normally by zero. However, if our instruction is jr or djnz, that
  637. ; number is actually a 2-bytes address that has to be relative to PC,
  638. ; so it's a special case. Let's check for this special case.
  639. bit 7, (ix+3)
  640. jr z, .absoluteValue ; bit not set? regular byte value,
  641. ; Our argument is a relative address ("e" type in djnz and jr). We have
  642. ; to subtract PC from it.
  643. ; First, check whether we're on first pass. If we are, skip processing
  644. ; below because not having real symbol value makes relative address
  645. ; verification falsely fail.
  646. inc c ; one extra byte is written
  647. call zasmIsFirstPass
  648. jr z, .end
  649. ; We're on second pass
  650. push de ; Don't let go of this, that's our dest
  651. push hl
  652. call zasmGetPC ; --> HL
  653. ex de, hl
  654. pop hl
  655. call intoHL
  656. dec hl ; what we write is "e-2"
  657. dec hl
  658. call subDEFromHL
  659. pop de ; Still have it? good
  660. ; HL contains our number and we'll check its bounds. If It's negative,
  661. ; H is going to be 0xff and L has to be >= 0x80. If it's positive,
  662. ; H is going to be 0 and L has to be < 0x80.
  663. ld a, l
  664. cp 0x80
  665. jr c, .skipHInc ; a < 0x80, H is expected to be 0
  666. ; A being >= 0x80 is only valid in cases where HL is negative and
  667. ; within bounds. This only happens is H == 0xff. Let's increase it to 0.
  668. inc h
  669. .skipHInc:
  670. ; Let's write our value now even though we haven't checked our bounds
  671. ; yet. This way, we don't have to store A somewhere else.
  672. ld (de), a
  673. ld a, h
  674. or a ; cp 0
  675. jr nz, .numberTruncated ; if A is anything but zero, we're out
  676. ; of bounds.
  677. jr .end
  678. .absoluteValue:
  679. ; verify that the MSB in argument is zero
  680. inc hl ; MSB is 2nd byte
  681. ld a, (hl)
  682. dec hl ; HL now points to LSB
  683. or a ; cp 0
  684. jr nz, .numberTruncated
  685. push bc
  686. ldi
  687. pop bc
  688. inc c
  689. jr .end
  690. .withWord:
  691. inc hl ; HL now points to LSB
  692. ; Clear to proceed. HL already points to our number
  693. push bc
  694. ldi ; LSB written, we point to MSB now
  695. ldi ; MSB written
  696. pop bc
  697. inc c ; two extra bytes are written
  698. inc c
  699. jr .end
  700. .numberTruncated:
  701. ; problem: not zero, so value is truncated. error
  702. ld c, 0
  703. .end:
  704. ld a, c
  705. pop bc
  706. pop hl
  707. pop de
  708. pop ix
  709. ret
  710. ; Parse argument in (HL) and place it in (DE)
  711. ; Sets Z on success, reset on error.
  712. processArg:
  713. call parseArg
  714. cp 0xff
  715. jr z, .error
  716. ld (de), a
  717. ; When A is a number, IX is set with the value of that number. Because
  718. ; We don't use the space allocated to store those numbers in any other
  719. ; occasion, we store IX there unconditonally, LSB first.
  720. inc de
  721. push hl
  722. push ix \ pop hl
  723. call writeHLinDE
  724. pop hl
  725. cp a ; ensure Z is set
  726. ret
  727. .error:
  728. call unsetZ
  729. ret
  730. ; Parse instruction specified in A (I_* const) with args in I/O and write
  731. ; resulting opcode(s) in I/O.
  732. ; Sets Z on success.
  733. parseInstruction:
  734. push bc
  735. push hl
  736. push de
  737. ; A is reused in matchPrimaryRow but that register is way too changing.
  738. ; Let's keep a copy in a more cosy register.
  739. ld c, a
  740. xor a
  741. ld (curArg1), a
  742. ld (curArg2), a
  743. call readWord
  744. jr nz, .nomorearg
  745. ld de, curArg1
  746. call processArg
  747. jr nz, .error
  748. call readComma
  749. jr nz, .nomorearg
  750. call readWord
  751. jr nz, .error
  752. ld de, curArg2
  753. call processArg
  754. jr nz, .error
  755. .nomorearg:
  756. ; Parsing done, no error, let's move forward to instr row matching!
  757. ld de, instrTBl
  758. ld b, INSTR_TBL_CNT
  759. .loop:
  760. ld a, c ; recall A param
  761. call matchPrimaryRow
  762. jr z, .match
  763. ld a, INSTR_TBL_ROWSIZE
  764. call addDE
  765. djnz .loop
  766. ; no match
  767. xor a
  768. jr .end
  769. .match:
  770. ; We have our matching instruction row. We're getting pretty near our
  771. ; goal here!
  772. call getUpcode
  773. or a ; is zero?
  774. jr z, .error
  775. ld b, a ; save output byte count
  776. ld hl, instrUpcode
  777. .loopWrite:
  778. ld a, (hl)
  779. call ioPutC
  780. inc hl
  781. djnz .loopWrite
  782. cp a ; ensure Z
  783. jr .end
  784. .error:
  785. call unsetZ
  786. .end:
  787. pop de
  788. pop hl
  789. pop bc
  790. ret
  791. ; In instruction metadata below, argument types arge indicated with a single
  792. ; char mnemonic that is called "argspec". This is the table of correspondance.
  793. ; Single letters are represented by themselves, so we don't need as much
  794. ; metadata.
  795. ; Special meaning:
  796. ; 0 : no arg
  797. ; 1-10 : group id (see Groups section)
  798. ; 0xff: error
  799. ; Format: 1 byte argspec + 4 chars string
  800. argspecTbl:
  801. .db 'A', "A", 0, 0, 0
  802. .db 'B', "B", 0, 0, 0
  803. .db 'C', "C", 0, 0, 0
  804. .db 'k', "(C)", 0
  805. .db 'D', "D", 0, 0, 0
  806. .db 'E', "E", 0, 0, 0
  807. .db 'H', "H", 0, 0, 0
  808. .db 'L', "L", 0, 0, 0
  809. .db 'I', "I", 0, 0, 0
  810. .db 'R', "R", 0, 0, 0
  811. .db 'h', "HL", 0, 0
  812. .db 'l', "(HL)"
  813. .db 'd', "DE", 0, 0
  814. .db 'e', "(DE)"
  815. .db 'b', "BC", 0, 0
  816. .db 'c', "(BC)"
  817. .db 'a', "AF", 0, 0
  818. .db 'f', "AF'", 0
  819. .db 'X', "IX", 0, 0
  820. .db 'Y', "IY", 0, 0
  821. .db 'x', "(IX)" ; always come with displacement
  822. .db 'y', "(IY)" ; with JP
  823. .db 's', "SP", 0, 0
  824. .db 'p', "(SP)"
  825. ; we also need argspecs for the condition flags
  826. .db 'Z', "Z", 0, 0, 0
  827. .db 'z', "NZ", 0, 0
  828. ; C is in conflict with the C register. The situation is ambiguous, but
  829. ; doesn't cause actual problems.
  830. .db '=', "NC", 0, 0
  831. .db '+', "P", 0, 0, 0
  832. .db '-', "M", 0, 0, 0
  833. .db '1', "PO", 0, 0
  834. .db '2', "PE", 0, 0
  835. ; argspecs not in the list:
  836. ; n -> N
  837. ; N -> NN
  838. ; m -> (N) (running out of mnemonics. 'm' for 'memory pointer')
  839. ; M -> (NN)
  840. ; Groups
  841. ; Groups are specified by strings of argspecs. To facilitate jumping to them,
  842. ; we have a fixed-sized table. Because most of them are 2 or 4 bytes long, we
  843. ; have a table that is 4 in size to minimize consumed space. We treat the two
  844. ; groups that take 8 bytes in a special way.
  845. ;
  846. ; The table below is in order, starting with group 0x01
  847. argGrpTbl:
  848. .db "bdha" ; 0x01
  849. .db "ZzC=" ; 0x02
  850. .db "bdhs" ; 0x03
  851. .db "bdXs" ; 0x04
  852. .db "bdYs" ; 0x05
  853. argGrpCC:
  854. .db "zZ=C12+-" ; 0xa
  855. argGrpABCDEHL:
  856. .db "BCDEHL_A" ; 0xb
  857. ; Each row is 4 bytes wide, fill with zeroes
  858. instrNames:
  859. .db "ADC", 0
  860. .db "ADD", 0
  861. .db "AND", 0
  862. .db "BIT", 0
  863. .db "CALL"
  864. .db "CCF", 0
  865. .db "CP",0,0
  866. .db "CPD", 0
  867. .db "CPDR"
  868. .db "CPI", 0
  869. .db "CPIR"
  870. .db "CPL", 0
  871. .db "DAA", 0
  872. .db "DEC", 0
  873. .db "DI",0,0
  874. .db "DJNZ"
  875. .db "EI",0,0
  876. .db "EX",0,0
  877. .db "EXX", 0
  878. .db "HALT"
  879. .db "IM",0,0
  880. .db "IN",0,0
  881. .db "INC", 0
  882. .db "IND", 0
  883. .db "INDR"
  884. .db "INI", 0
  885. .db "INIR"
  886. .db "JP",0,0
  887. .db "JR",0,0
  888. .db "LD",0,0
  889. .db "LDD", 0
  890. .db "LDDR"
  891. .db "LDI", 0
  892. .db "LDIR"
  893. .db "NEG", 0
  894. .db "NOP", 0
  895. .db "OR",0,0
  896. .db "OTDR"
  897. .db "OTIR"
  898. .db "OUT", 0
  899. .db "POP", 0
  900. .db "PUSH"
  901. .db "RET", 0
  902. .db "RETI"
  903. .db "RETN"
  904. .db "RL", 0, 0
  905. .db "RLA", 0
  906. .db "RLC", 0
  907. .db "RLCA"
  908. .db "RR", 0, 0
  909. .db "RRA", 0
  910. .db "RRC", 0
  911. .db "RRCA"
  912. .db "SBC", 0
  913. .db "SCF", 0
  914. .db "SUB", 0
  915. .db "XOR", 0
  916. ; This is a list of all supported instructions. Each row represent a combination
  917. ; of instr/argspecs (which means more than one row per instr). Format:
  918. ;
  919. ; 1 byte for the instruction ID
  920. ; 1 byte for arg constant
  921. ; 1 byte for 2nd arg constant
  922. ; 1 byte displacement for group arguments + flags
  923. ; 2 bytes for upcode (2nd byte is zero if instr is one byte)
  924. ;
  925. ; An "arg constant" is a char corresponding to either a row in argspecTbl or
  926. ; a group index in argGrpTbl (values < 0x10 are considered group indexes).
  927. ;
  928. ; The displacement bit is split in 2 nibbles: lower nibble is the displacement
  929. ; value, upper nibble is for flags:
  930. ;
  931. ; Bit 7: indicates that the numerical argument is of the 'e' type and has to be
  932. ; decreased by 2 (djnz, jr).
  933. ; Bit 6: it indicates that the group argument's value is to be placed on the
  934. ; second upcode rather than the first.
  935. ; Bit 5: Indicates that this row is handled very specially: the next two bytes
  936. ; aren't upcode bytes, but a routine address to call to handle this case with
  937. ; custom code.
  938. instrTBl:
  939. .db I_ADC, 'A', 'l', 0, 0x8e , 0 ; ADC A, (HL)
  940. .db I_ADC, 'A', 0xb, 0, 0b10001000 , 0 ; ADC A, r
  941. .db I_ADC, 'A', 'n', 0, 0xce , 0 ; ADC A, n
  942. .db I_ADC, 'h', 0x3, 0x44, 0xed, 0b01001010 ; ADC HL, ss
  943. .db I_ADD, 'A', 'l', 0, 0x86 , 0 ; ADD A, (HL)
  944. .db I_ADD, 'A', 0xb, 0, 0b10000000 , 0 ; ADD A, r
  945. .db I_ADD, 'A', 'n', 0, 0xc6 , 0 ; ADD A, n
  946. .db I_ADD, 'h', 0x3, 4, 0b00001001 , 0 ; ADD HL, ss
  947. .db I_ADD, 'X', 0x4, 0x44, 0xdd, 0b00001001 ; ADD IX, pp
  948. .db I_ADD, 'Y', 0x5, 0x44, 0xfd, 0b00001001 ; ADD IY, rr
  949. .db I_ADD, 'A', 'x', 0, 0xdd, 0x86 ; ADD A, (IX+d)
  950. .db I_ADD, 'A', 'y', 0, 0xfd, 0x86 ; ADD A, (IY+d)
  951. .db I_AND, 'l', 0, 0, 0xa6 , 0 ; AND (HL)
  952. .db I_AND, 0xb, 0, 0, 0b10100000 , 0 ; AND r
  953. .db I_AND, 'n', 0, 0, 0xe6 , 0 ; AND n
  954. .db I_AND, 'x', 0, 0, 0xdd, 0xa6 ; AND (IX+d)
  955. .db I_AND, 'y', 0, 0, 0xfd, 0xa6 ; AND (IY+d)
  956. .db I_BIT, 'n', 'l', 0x20 \ .dw handleBITHL ; BIT b, (HL)
  957. .db I_BIT, 'n', 'x', 0x20 \ .dw handleBITIX ; BIT b, (IX+d)
  958. .db I_BIT, 'n', 'y', 0x20 \ .dw handleBITIY ; BIT b, (IY+d)
  959. .db I_BIT, 'n', 0xb, 0x20 \ .dw handleBITR ; BIT b, r
  960. .db I_CALL,0xa, 'N', 3, 0b11000100 , 0 ; CALL cc, NN
  961. .db I_CALL,'N', 0, 0, 0xcd , 0 ; CALL NN
  962. .db I_CCF, 0, 0, 0, 0x3f , 0 ; CCF
  963. .db I_CP, 'l', 0, 0, 0xbe , 0 ; CP (HL)
  964. .db I_CP, 0xb, 0, 0, 0b10111000 , 0 ; CP r
  965. .db I_CP, 'n', 0, 0, 0xfe , 0 ; CP n
  966. .db I_CP, 'x', 0, 0, 0xdd, 0xbe ; CP (IX+d)
  967. .db I_CP, 'y', 0, 0, 0xfd, 0xbe ; CP (IY+d)
  968. .db I_CPD, 0, 0, 0, 0xed, 0xa9 ; CPD
  969. .db I_CPDR,0, 0, 0, 0xed, 0xb9 ; CPDR
  970. .db I_CPI, 0, 0, 0, 0xed, 0xa1 ; CPI
  971. .db I_CPIR,0, 0, 0, 0xed, 0xb1 ; CPIR
  972. .db I_CPL, 0, 0, 0, 0x2f , 0 ; CPL
  973. .db I_DAA, 0, 0, 0, 0x27 , 0 ; DAA
  974. .db I_DEC, 'l', 0, 0, 0x35 , 0 ; DEC (HL)
  975. .db I_DEC, 'X', 0, 0, 0xdd, 0x2b ; DEC IX
  976. .db I_DEC, 'x', 0, 0, 0xdd, 0x35 ; DEC (IX+d)
  977. .db I_DEC, 'Y', 0, 0, 0xfd, 0x2b ; DEC IY
  978. .db I_DEC, 'y', 0, 0, 0xfd, 0x35 ; DEC (IY+d)
  979. .db I_DEC, 0xb, 0, 3, 0b00000101 , 0 ; DEC r
  980. .db I_DEC, 0x3, 0, 4, 0b00001011 , 0 ; DEC ss
  981. .db I_DI, 0, 0, 0, 0xf3 , 0 ; DI
  982. .db I_DJNZ,'n', 0, 0x80, 0x10 , 0 ; DJNZ e
  983. .db I_EI, 0, 0, 0, 0xfb , 0 ; EI
  984. .db I_EX, 'p', 'h', 0, 0xe3 , 0 ; EX (SP), HL
  985. .db I_EX, 'p', 'X', 0, 0xdd, 0xe3 ; EX (SP), IX
  986. .db I_EX, 'p', 'Y', 0, 0xfd, 0xe3 ; EX (SP), IY
  987. .db I_EX, 'a', 'f', 0, 0x08 , 0 ; EX AF, AF'
  988. .db I_EX, 'd', 'h', 0, 0xeb , 0 ; EX DE, HL
  989. .db I_EXX, 0, 0, 0, 0xd9 , 0 ; EXX
  990. .db I_HALT,0, 0, 0, 0x76 , 0 ; HALT
  991. .db I_IM, 'n', 0, 0x20 \ .dw handleIM ; IM {0,1,2}
  992. .db I_IN, 'A', 'm', 0, 0xdb , 0 ; IN A, (n)
  993. .db I_IN, 0xb, 'k', 0x43, 0xed, 0b01000000 ; IN r, (C)
  994. .db I_INC, 'l', 0, 0, 0x34 , 0 ; INC (HL)
  995. .db I_INC, 'X', 0, 0, 0xdd , 0x23 ; INC IX
  996. .db I_INC, 'x', 0, 0, 0xdd , 0x34 ; INC (IX+d)
  997. .db I_INC, 'Y', 0, 0, 0xfd , 0x23 ; INC IY
  998. .db I_INC, 'y', 0, 0, 0xfd , 0x34 ; INC (IY+d)
  999. .db I_INC, 0xb, 0, 3, 0b00000100 , 0 ; INC r
  1000. .db I_INC, 0x3, 0, 4, 0b00000011 , 0 ; INC ss
  1001. .db I_IND, 0, 0, 0, 0xed, 0xaa ; IND
  1002. .db I_INDR,0, 0, 0, 0xed, 0xba ; INDR
  1003. .db I_INI, 0, 0, 0, 0xed, 0xa2 ; INI
  1004. .db I_INIR,0, 0, 0, 0xed, 0xb2 ; INIR
  1005. .db I_JP, 'l', 0, 0, 0xe9 , 0 ; JP (HL)
  1006. .db I_JP, 0xa, 'N', 3, 0b11000010 , 0 ; JP cc, NN
  1007. .db I_JP, 'N', 0, 0, 0xc3 , 0 ; JP NN
  1008. .db I_JP, 'x', 0, 0x20 \ .dw handleJPIX ; JP (IX)
  1009. .db I_JP, 'y', 0, 0x20 \ .dw handleJPIY ; JP (IY)
  1010. .db I_JR, 'n', 0, 0x80, 0x18 , 0 ; JR e
  1011. .db I_JR, 'C', 'n', 0x80, 0x38 , 0 ; JR C, e
  1012. .db I_JR, '=', 'n', 0x80, 0x30 , 0 ; JR NC, e
  1013. .db I_JR, 'Z', 'n', 0x80, 0x28 , 0 ; JR Z, e
  1014. .db I_JR, 'z', 'n', 0x80, 0x20 , 0 ; JR NZ, e
  1015. .db I_LD, 'c', 'A', 0, 0x02 , 0 ; LD (BC), A
  1016. .db I_LD, 'e', 'A', 0, 0x12 , 0 ; LD (DE), A
  1017. .db I_LD, 'A', 'c', 0, 0x0a , 0 ; LD A, (BC)
  1018. .db I_LD, 'A', 'e', 0, 0x1a , 0 ; LD A, (DE)
  1019. .db I_LD, 's', 'h', 0, 0xf9 , 0 ; LD SP, HL
  1020. .db I_LD, 'A', 'I', 0, 0xed, 0x57 ; LD A, I
  1021. .db I_LD, 'I', 'A', 0, 0xed, 0x47 ; LD I, A
  1022. .db I_LD, 'A', 'R', 0, 0xed, 0x5f ; LD A, R
  1023. .db I_LD, 'R', 'A', 0, 0xed, 0x4f ; LD R, A
  1024. .db I_LD, 'l', 0xb, 0, 0b01110000 , 0 ; LD (HL), r
  1025. .db I_LD, 0xb, 'l', 3, 0b01000110 , 0 ; LD r, (HL)
  1026. .db I_LD, 'l', 'n', 0, 0x36 , 0 ; LD (HL), n
  1027. .db I_LD, 0xb, 'n', 3, 0b00000110 , 0 ; LD r, n
  1028. .db I_LD, 0xb, 0xb, 0x20 \ .dw handleLDrr ; LD r, r'
  1029. .db I_LD, 0x3, 'N', 4, 0b00000001 , 0 ; LD dd, nn
  1030. .db I_LD, 'X', 'N', 0, 0xdd, 0x21 ; LD IX, NN
  1031. .db I_LD, 'Y', 'N', 0, 0xfd, 0x21 ; LD IY, NN
  1032. .db I_LD, 'M', 'A', 0, 0x32 , 0 ; LD (NN), A
  1033. .db I_LD, 'A', 'M', 0, 0x3a , 0 ; LD A, (NN)
  1034. .db I_LD, 'M', 'h', 0, 0x22 , 0 ; LD (NN), HL
  1035. .db I_LD, 'h', 'M', 0, 0x2a , 0 ; LD HL, (NN)
  1036. .db I_LD, 'M', 'X', 0, 0xdd, 0x22 ; LD (NN), IX
  1037. .db I_LD, 'X', 'M', 0, 0xdd, 0x2a ; LD IX, (NN)
  1038. .db I_LD, 'M', 'Y', 0, 0xfd, 0x22 ; LD (NN), IY
  1039. .db I_LD, 'Y', 'M', 0, 0xfd, 0x2a ; LD IY, (NN)
  1040. .db I_LD, 'M', 0x3, 0x44, 0xed, 0b01000011 ; LD (NN), dd
  1041. .db I_LD, 0x3, 'M', 0x44, 0xed, 0b01001011 ; LD dd, (NN)
  1042. .db I_LD, 'x', 'n', 0x20 \ .dw handleLDIXn ; LD (IX+d), n
  1043. .db I_LD, 'y', 'n', 0x20 \ .dw handleLDIYn ; LD (IY+d), n
  1044. .db I_LD, 'x', 0xb, 0x20 \ .dw handleLDIXr ; LD (IX+d), r
  1045. .db I_LD, 'y', 0xb, 0x20 \ .dw handleLDIYr ; LD (IY+d), r
  1046. .db I_LD, 0xb, 'x', 0x20 \ .dw handleLDrIX ; LD r, (IX+d)
  1047. .db I_LD, 0xb, 'y', 0x20 \ .dw handleLDrIY ; LD r, (IY+d)
  1048. .db I_LDD, 0, 0, 0, 0xed, 0xa8 ; LDD
  1049. .db I_LDDR,0, 0, 0, 0xed, 0xb8 ; LDDR
  1050. .db I_LDI, 0, 0, 0, 0xed, 0xa0 ; LDI
  1051. .db I_LDIR,0, 0, 0, 0xed, 0xb0 ; LDIR
  1052. .db I_NEG, 0, 0, 0, 0xed, 0x44 ; NEG
  1053. .db I_NOP, 0, 0, 0, 0x00 , 0 ; NOP
  1054. .db I_OR, 'l', 0, 0, 0xb6 , 0 ; OR (HL)
  1055. .db I_OR, 0xb, 0, 0, 0b10110000 , 0 ; OR r
  1056. .db I_OR, 'n', 0, 0, 0xf6 , 0 ; OR n
  1057. .db I_OR, 'x', 0, 0, 0xdd, 0xb6 ; OR (IX+d)
  1058. .db I_OR, 'y', 0, 0, 0xfd, 0xb6 ; OR (IY+d)
  1059. .db I_OTDR,0, 0, 0, 0xed, 0xbb ; OTDR
  1060. .db I_OTIR,0, 0, 0, 0xed, 0xb3 ; OTIR
  1061. .db I_OUT, 'm', 'A', 0, 0xd3 , 0 ; OUT (n), A
  1062. .db I_OUT, 'k', 0xb, 0x43, 0xed, 0b01000001 ; OUT (C), r
  1063. .db I_POP, 'X', 0, 0, 0xdd, 0xe1 ; POP IX
  1064. .db I_POP, 'Y', 0, 0, 0xfd, 0xe1 ; POP IY
  1065. .db I_POP, 0x1, 0, 4, 0b11000001 , 0 ; POP qq
  1066. .db I_PUSH,'X', 0, 0, 0xdd, 0xe5 ; PUSH IX
  1067. .db I_PUSH,'Y', 0, 0, 0xfd, 0xe5 ; PUSH IY
  1068. .db I_PUSH,0x1, 0, 4, 0b11000101 , 0 ; PUSH qq
  1069. .db I_RET, 0, 0, 0, 0xc9 , 0 ; RET
  1070. .db I_RET, 0xa, 0, 3, 0b11000000 , 0 ; RET cc
  1071. .db I_RETI,0, 0, 0, 0xed, 0x4d ; RETI
  1072. .db I_RETN,0, 0, 0, 0xed, 0x45 ; RETN
  1073. .db I_RL, 0xb, 0,0x40, 0xcb, 0b00010000 ; RL r
  1074. .db I_RLA, 0, 0, 0, 0x17 , 0 ; RLA
  1075. .db I_RLC, 0xb, 0,0x40, 0xcb, 0b00000000 ; RLC r
  1076. .db I_RLCA,0, 0, 0, 0x07 , 0 ; RLCA
  1077. .db I_RR, 0xb, 0,0x40, 0xcb, 0b00011000 ; RR r
  1078. .db I_RRA, 0, 0, 0, 0x1f , 0 ; RRA
  1079. .db I_RRC, 0xb, 0,0x40, 0xcb, 0b00001000 ; RRC r
  1080. .db I_RRCA,0, 0, 0, 0x0f , 0 ; RRCA
  1081. .db I_SBC, 'A', 'l', 0, 0x9e , 0 ; SBC A, (HL)
  1082. .db I_SBC, 'A', 0xb, 0, 0b10011000 , 0 ; SBC A, r
  1083. .db I_SBC,'h',0x3,0x44, 0xed, 0b01000010 ; SBC HL, ss
  1084. .db I_SCF, 0, 0, 0, 0x37 , 0 ; SCF
  1085. .db I_SUB, 'l', 0, 0, 0x96 , 0 ; SUB (HL)
  1086. .db I_SUB, 0xb, 0, 0, 0b10010000 , 0 ; SUB r
  1087. .db I_SUB, 'n', 0, 0, 0xd6 , 0 ; SUB n
  1088. .db I_XOR, 'l', 0, 0, 0xae , 0 ; XOR (HL)
  1089. .db I_XOR, 0xb, 0, 0, 0b10101000 , 0 ; XOR r
  1090. ; *** Variables ***
  1091. ; Args are 3 bytes: argspec, then values of numerical constants (when that's
  1092. ; appropriate)
  1093. curArg1:
  1094. .db 0, 0, 0
  1095. curArg2:
  1096. .db 0, 0, 0
  1097. instrUpcode:
  1098. .db 0, 0, 0, 0