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.

cmd.asm 3.8KB

5 years ago
Decimal parse optimisations (#45) * Optimised parsing functions and other minor optimisations UnsetZ has been reduced by a byte, and between 17 and 28 cycles saved based on branching. Since branching is based on a being 0, it shouldn't have to branch very often and so be 28 cycles saved most the time. Including the initial call, the old version was 60 cycles, so this should be nearly twice as fast. fmtHex has been reduced by 4 bytes and between 3 and 8 cycles based on branching. fmtHexPair had a redundant "and" removed, saving two bytes and seven cycles. parseHex has been reduced by 7 bytes. Due to so much branching, it's hard to say if it's faster, but it should be since it's fewer operations and now conditional returns are used which are a cycle faster than conditional jumps. I think there's more to improve here, but I haven't come up with anything yet. * Major parsing optimisations Totally reworked both parseDecimal and parseDecimalDigit parseDecimalDigit no longer exists, as it could be replaced by an inline alternative in the 4 places it appeared. This saves one byte overall, as the inline version is 4 bytes, 1 byte more than a call, and removing the function saved 5 bytes. It has been reduced from between 52 and 35 cycles (35 on error, so we'd expect 52 cycles to be more common unless someone's really bad at programming) to 14 cycles, so 2-3 times faster. parseDecimal has been reduced by a byte, and now the main loop is just about twice as fast, but with increased overhead. To put this into perspective, if we ignore error cases: For decimals of length 1 it'll be 1.20x faster, for decimals of length 2, 1.41x faster, for length 3, 1.51x faster, for length 4, 1.57x faster, and for length 5 and above, at least 1.48x faster (even faster if there's leading zeroes or not the worst case scenario). I believe there is still room for improvement, since the first iteration can be nearly replaced with "ld l, c" since 0*10=0, but when I tried this I could either add a zero check into the main loop, adding around 40 cycles and 10 bytes, or add 20 bytes to the overhead, and I don't think either of those options are worth it. * Inlined parseDecimalDigit See previous commit, and /lib/parse.asm, for details * Fixed tabs and spacing * Fixed tabs and spacing * Better explanation and layout * Corrected error in comments, and a new parseHex 5 bytes saved in parseHex, again hard to say what that does to speed, the shortest possible speed is probably a little slower but I think non-error cases should be around 9 cycles faster for decimal and 18 cycles faster for hex as there's now only two conditional returns and no compliment carries. * Fixed the new parseHex I accidentally did `add 0xe9` without specifying `a` * Commented the use of daa I made the comments surrounding my use of daa much clearer, so it isn't quite so mystical what's being done here. * Removed skip leading zeroes, added skip first multiply Now instead of skipping leading zeroes, the first digit is loaded directly into hl without first multiplying by 10. This means the first loop is skipped in the overhead, making the method 2-3 times faster overall, and is now faster for the more common fewer digit cases too. The number of bytes is exactly the same, and the inner loop is slightly faster too thanks to no longer needing to load a into c. To be more precise about the speed increase over the current code, for decimals of length 1 it'll be 3.18x faster, for decimals of length 2, 2.50x faster, for length 3, 2.31x faster, for length 4, 2.22x faster, and for length 5 and above, at least 2.03x faster. In terms of cycles, this is around 100+(132*length) cycles saved per decimal. * Fixed erroring out for all number >0x1999 I fixed the errors for numbers >0x1999, sadly it is now 6 bytes bigger, so 5 bytes larger than the original, but the speed increases should still hold. * Fixed more errors, clearer choice of constants * Clearer choice of constants * Moved and indented comment about fmtHex's method * Marked inlined parseDecimalDigit uses * Renamed .error, removed trailing whitespace, more verbose comments.
4 years ago
Decimal parse optimisations (#45) * Optimised parsing functions and other minor optimisations UnsetZ has been reduced by a byte, and between 17 and 28 cycles saved based on branching. Since branching is based on a being 0, it shouldn't have to branch very often and so be 28 cycles saved most the time. Including the initial call, the old version was 60 cycles, so this should be nearly twice as fast. fmtHex has been reduced by 4 bytes and between 3 and 8 cycles based on branching. fmtHexPair had a redundant "and" removed, saving two bytes and seven cycles. parseHex has been reduced by 7 bytes. Due to so much branching, it's hard to say if it's faster, but it should be since it's fewer operations and now conditional returns are used which are a cycle faster than conditional jumps. I think there's more to improve here, but I haven't come up with anything yet. * Major parsing optimisations Totally reworked both parseDecimal and parseDecimalDigit parseDecimalDigit no longer exists, as it could be replaced by an inline alternative in the 4 places it appeared. This saves one byte overall, as the inline version is 4 bytes, 1 byte more than a call, and removing the function saved 5 bytes. It has been reduced from between 52 and 35 cycles (35 on error, so we'd expect 52 cycles to be more common unless someone's really bad at programming) to 14 cycles, so 2-3 times faster. parseDecimal has been reduced by a byte, and now the main loop is just about twice as fast, but with increased overhead. To put this into perspective, if we ignore error cases: For decimals of length 1 it'll be 1.20x faster, for decimals of length 2, 1.41x faster, for length 3, 1.51x faster, for length 4, 1.57x faster, and for length 5 and above, at least 1.48x faster (even faster if there's leading zeroes or not the worst case scenario). I believe there is still room for improvement, since the first iteration can be nearly replaced with "ld l, c" since 0*10=0, but when I tried this I could either add a zero check into the main loop, adding around 40 cycles and 10 bytes, or add 20 bytes to the overhead, and I don't think either of those options are worth it. * Inlined parseDecimalDigit See previous commit, and /lib/parse.asm, for details * Fixed tabs and spacing * Fixed tabs and spacing * Better explanation and layout * Corrected error in comments, and a new parseHex 5 bytes saved in parseHex, again hard to say what that does to speed, the shortest possible speed is probably a little slower but I think non-error cases should be around 9 cycles faster for decimal and 18 cycles faster for hex as there's now only two conditional returns and no compliment carries. * Fixed the new parseHex I accidentally did `add 0xe9` without specifying `a` * Commented the use of daa I made the comments surrounding my use of daa much clearer, so it isn't quite so mystical what's being done here. * Removed skip leading zeroes, added skip first multiply Now instead of skipping leading zeroes, the first digit is loaded directly into hl without first multiplying by 10. This means the first loop is skipped in the overhead, making the method 2-3 times faster overall, and is now faster for the more common fewer digit cases too. The number of bytes is exactly the same, and the inner loop is slightly faster too thanks to no longer needing to load a into c. To be more precise about the speed increase over the current code, for decimals of length 1 it'll be 3.18x faster, for decimals of length 2, 2.50x faster, for length 3, 2.31x faster, for length 4, 2.22x faster, and for length 5 and above, at least 2.03x faster. In terms of cycles, this is around 100+(132*length) cycles saved per decimal. * Fixed erroring out for all number >0x1999 I fixed the errors for numbers >0x1999, sadly it is now 6 bytes bigger, so 5 bytes larger than the original, but the speed increases should still hold. * Fixed more errors, clearer choice of constants * Clearer choice of constants * Moved and indented comment about fmtHex's method * Marked inlined parseDecimalDigit uses * Renamed .error, removed trailing whitespace, more verbose comments.
4 years ago
Decimal parse optimisations (#45) * Optimised parsing functions and other minor optimisations UnsetZ has been reduced by a byte, and between 17 and 28 cycles saved based on branching. Since branching is based on a being 0, it shouldn't have to branch very often and so be 28 cycles saved most the time. Including the initial call, the old version was 60 cycles, so this should be nearly twice as fast. fmtHex has been reduced by 4 bytes and between 3 and 8 cycles based on branching. fmtHexPair had a redundant "and" removed, saving two bytes and seven cycles. parseHex has been reduced by 7 bytes. Due to so much branching, it's hard to say if it's faster, but it should be since it's fewer operations and now conditional returns are used which are a cycle faster than conditional jumps. I think there's more to improve here, but I haven't come up with anything yet. * Major parsing optimisations Totally reworked both parseDecimal and parseDecimalDigit parseDecimalDigit no longer exists, as it could be replaced by an inline alternative in the 4 places it appeared. This saves one byte overall, as the inline version is 4 bytes, 1 byte more than a call, and removing the function saved 5 bytes. It has been reduced from between 52 and 35 cycles (35 on error, so we'd expect 52 cycles to be more common unless someone's really bad at programming) to 14 cycles, so 2-3 times faster. parseDecimal has been reduced by a byte, and now the main loop is just about twice as fast, but with increased overhead. To put this into perspective, if we ignore error cases: For decimals of length 1 it'll be 1.20x faster, for decimals of length 2, 1.41x faster, for length 3, 1.51x faster, for length 4, 1.57x faster, and for length 5 and above, at least 1.48x faster (even faster if there's leading zeroes or not the worst case scenario). I believe there is still room for improvement, since the first iteration can be nearly replaced with "ld l, c" since 0*10=0, but when I tried this I could either add a zero check into the main loop, adding around 40 cycles and 10 bytes, or add 20 bytes to the overhead, and I don't think either of those options are worth it. * Inlined parseDecimalDigit See previous commit, and /lib/parse.asm, for details * Fixed tabs and spacing * Fixed tabs and spacing * Better explanation and layout * Corrected error in comments, and a new parseHex 5 bytes saved in parseHex, again hard to say what that does to speed, the shortest possible speed is probably a little slower but I think non-error cases should be around 9 cycles faster for decimal and 18 cycles faster for hex as there's now only two conditional returns and no compliment carries. * Fixed the new parseHex I accidentally did `add 0xe9` without specifying `a` * Commented the use of daa I made the comments surrounding my use of daa much clearer, so it isn't quite so mystical what's being done here. * Removed skip leading zeroes, added skip first multiply Now instead of skipping leading zeroes, the first digit is loaded directly into hl without first multiplying by 10. This means the first loop is skipped in the overhead, making the method 2-3 times faster overall, and is now faster for the more common fewer digit cases too. The number of bytes is exactly the same, and the inner loop is slightly faster too thanks to no longer needing to load a into c. To be more precise about the speed increase over the current code, for decimals of length 1 it'll be 3.18x faster, for decimals of length 2, 2.50x faster, for length 3, 2.31x faster, for length 4, 2.22x faster, and for length 5 and above, at least 2.03x faster. In terms of cycles, this is around 100+(132*length) cycles saved per decimal. * Fixed erroring out for all number >0x1999 I fixed the errors for numbers >0x1999, sadly it is now 6 bytes bigger, so 5 bytes larger than the original, but the speed increases should still hold. * Fixed more errors, clearer choice of constants * Clearer choice of constants * Moved and indented comment about fmtHex's method * Marked inlined parseDecimalDigit uses * Renamed .error, removed trailing whitespace, more verbose comments.
4 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. ; cmd - parse and interpret command
  2. ;
  3. ; *** Consts ***
  4. ; address type
  5. .equ ABSOLUTE 0
  6. ; handles +, - and ".". For +, easy. For -, addr is negative. For ., it's 0.
  7. .equ RELATIVE 1
  8. .equ EOF 2
  9. ; *** Variables ***
  10. ; An address is a one byte type and a two bytes line number (0-indexed)
  11. .equ CMD_ADDR1 CMD_RAMSTART
  12. .equ CMD_ADDR2 @+3
  13. .equ CMD_TYPE @+3
  14. .equ CMD_RAMEND @+1
  15. ; *** Code ***
  16. ; Parse command line that HL points to and set unit's variables
  17. ; Sets Z on success, unset on error.
  18. cmdParse:
  19. ld a, (hl)
  20. cp 'q'
  21. jr z, .simpleCmd
  22. cp 'w'
  23. jr z, .simpleCmd
  24. ld ix, CMD_ADDR1
  25. call .readAddr
  26. ret nz
  27. ; Before we check for the existence of a second addr, let's set that
  28. ; second addr to the same value as the first. That's going to be its
  29. ; value if we have to ",".
  30. ld a, (ix)
  31. ld (CMD_ADDR2), a
  32. ld a, (ix+1)
  33. ld (CMD_ADDR2+1), a
  34. ld a, (ix+2)
  35. ld (CMD_ADDR2+2), a
  36. ld a, (hl)
  37. cp ','
  38. jr nz, .noaddr2
  39. inc hl
  40. ld ix, CMD_ADDR2
  41. call .readAddr
  42. ret nz
  43. .noaddr2:
  44. ; We expect HL (rest of the cmdline) to be a null char or an accepted
  45. ; cmd, otherwise it's garbage
  46. ld a, (hl)
  47. or a
  48. jr z, .nullCmd
  49. cp 'p'
  50. jr z, .okCmd
  51. cp 'd'
  52. jr z, .okCmd
  53. cp 'a'
  54. jr z, .okCmd
  55. cp 'i'
  56. jr z, .okCmd
  57. ; unsupported cmd
  58. ret ; Z unset
  59. .nullCmd:
  60. ld a, 'p'
  61. .okCmd:
  62. ld (CMD_TYPE), a
  63. ret ; Z already set
  64. .simpleCmd:
  65. ; Z already set
  66. ld (CMD_TYPE), a
  67. ret
  68. ; Parse the string at (HL) and sets its corresponding address in IX, properly
  69. ; considering implicit values (current address when nothing is specified).
  70. ; advances HL to the char next to the last parsed char.
  71. ; It handles "+" and "-" addresses such as "+3", "-2", "+", "-".
  72. ; Sets Z on success, unset on error. Line out of bounds isn't an error. Only
  73. ; overflows.
  74. .readAddr:
  75. ld a, (hl)
  76. cp '+'
  77. jr z, .plusOrMinus
  78. cp '-'
  79. jr z, .plusOrMinus
  80. cp '.'
  81. jr z, .dot
  82. cp '$'
  83. jr z, .eof
  84. ; inline parseDecimalDigit
  85. add a, 0xff-'9' ; maps '0'-'9' onto 0xf6-0xff
  86. sub 0xff-9 ; maps to 0-9 and carries if not a digit
  87. jr c, .notHandled
  88. ; straight number
  89. ld a, ABSOLUTE
  90. ld (ix), a
  91. call .parseDecimalM
  92. ret nz
  93. dec de ; from 1-based to 0-base
  94. jr .end
  95. .dot:
  96. inc hl ; advance cmd cursor
  97. ; the rest is the same as .notHandled
  98. .notHandled:
  99. ; something else. It's probably our command. Our addr is therefore "."
  100. ld a, RELATIVE
  101. ld (ix), a
  102. xor a ; sets Z
  103. ld (ix+1), a
  104. ld (ix+2), a
  105. ret
  106. .eof:
  107. inc hl ; advance cmd cursor
  108. ld a, EOF
  109. ld (ix), a
  110. ret ; Z set during earlier CP
  111. .plusOrMinus:
  112. push af ; preserve that + or -
  113. ld a, RELATIVE
  114. ld (ix), a
  115. inc hl ; advance cmd cursor
  116. ld a, (hl)
  117. ld de, 1 ; if .pmNoSuffix
  118. ; inline parseDecimalDigit
  119. add a, 0xff-'9' ; maps '0'-'9' onto 0xf6-0xff
  120. sub 0xff-9 ; maps to 0-9 and carries if not a digit
  121. jr c, .pmNoSuffix
  122. call .parseDecimalM ; --> DE
  123. .pmNoSuffix:
  124. pop af ; bring back that +/-
  125. cp '-'
  126. jr nz, .end
  127. ; we had a "-". Negate DE
  128. push hl
  129. ld hl, 0
  130. sbc hl, de
  131. ex de, hl
  132. pop hl
  133. .end:
  134. ; we still have to save DE in memory
  135. ld (ix+1), e
  136. ld (ix+2), d
  137. cp a ; ensure Z
  138. ret
  139. ; call parseDecimal and set HL to the character following the last digit
  140. .parseDecimalM:
  141. push bc
  142. push ix
  143. push hl
  144. .loop:
  145. inc hl
  146. ld a, (hl)
  147. ; inline parseDecimalDigit
  148. add a, 0xff-'9' ; maps '0'-'9' onto 0xf6-0xff
  149. sub 0xff-9 ; maps to 0-9 and carries if not a digit
  150. jr nc, .loop
  151. ; We're at the first non-digit char. Let's save it because we're going
  152. ; to temporarily replace it with a null.
  153. ld b, (hl) ; refetch (HL), A has been mucked with in
  154. ; parseDecimalDigit
  155. xor a
  156. ld (hl), a
  157. ; Now, let's go back to the beginning of the string and parse it.
  158. ; but before we do this, let's save the end of string in DE
  159. ex de, hl
  160. pop hl
  161. call parseDecimal
  162. ; Z is set properly at this point. nothing touches Z below.
  163. ld a, b
  164. ld (de), a
  165. ex de, hl ; put end of string back from DE to HL
  166. ; Put addr in its final register, DE
  167. push ix \ pop de
  168. pop ix
  169. pop bc
  170. ret