rtdrv.s 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. # rtdrv.s -- call runtime entries, with known arguments, and report what
  2. # happened. Assembled by tests/rt_exec.py into a complete .COM image.
  3. #
  4. # This replaces the Unicorn harness. The expectations did not change; only the
  5. # machine did. Everything here exists because qemu, unlike an emulator with a
  6. # Python API, gives you one thing: a serial port. So a check has to be
  7. # expressed as a program that reports on itself.
  8. #
  9. # The report is a framed record on the serial port:
  10. #
  11. # 01 <case index, 2 hex digits> 02 <whatever the entries printed> 03
  12. # <dumplen bytes at dumpaddr, 2 hex digits each> 04
  13. #
  14. # The four control bytes occur in no expected output, and rt_exec.py REFUSES to
  15. # run a case whose expectation contains one -- so a marker can never be mistaken
  16. # for output, and a driver that emitted nothing is a parse failure rather than
  17. # a silent pass.
  18. #
  19. # Why one qemu boot per check instead of one boot for all of them: the runtime
  20. # keeps state that outlives a call -- the one-character pushback slot, and the
  21. # input cursor the boot sector's INT 21h shim owns. One boot for everything
  22. # would make each check depend on the ones before it, so a check could fail
  23. # because of its neighbour and a green run would prove less than it appears to.
  24. # A boot costs about a tenth of a second and 35 of them cost under four, which
  25. # buys every check a machine that has provably never executed anything. (The
  26. # input descriptor is still re-armed on every boot, because it lives in the
  27. # image and the image is the same bytes each time.)
  28. #
  29. # Two things about the 8086 shape the code below, and both were found by
  30. # running it rather than by reading it:
  31. #
  32. # * Every entry preserves BP and SP and NO other register. So the cursor
  33. # into the case record is BP: a loop counter in CX or DI would be destroyed
  34. # by the first call. The entry's own BP frame is pushed and restored, so
  35. # our BP comes back.
  36. #
  37. # * `movb n(%bp), %cl' leaves the HIGH byte of CX exactly as it was. Reading
  38. # the input length that way and then writing CX to INLEN handed the boot
  39. # sector's INT 21h shim a 16-bit length with an undefined top half. The
  40. # 8086 has no memory-to-memory move and no zero-extending byte load, so the
  41. # high half has to be cleared by hand, every time.
  42. #
  43. # * `dec' sets the sign flag from the RESULT, so `decw cnt / js done' runs the
  44. # body for cnt = 1 (1 -> 0, SF clear) and stops on the wrap (0 -> FFFF).
  45. # It reads like an off-by-one and is not; the comment at the loop says so
  46. # again because the next person will wonder.
  47. #
  48. # The image is assembled in ONE piece, here, so every address is a label or an
  49. # .org and nothing has to be agreed with Python in two places. What Python does
  50. # have to agree about -- the runtime's base, and the input descriptor -- is
  51. # checked rather than trusted: it assembles this file and then asserts that the
  52. # blob it got from RtProbe really is at RT_BASE in the bytes that came out.
  53. .set LOAD_BIAS, 0x0100 # a .COM lives at CS:0100 (that is the PSP)
  54. .set SOH, 0x01
  55. .set STX, 0x02
  56. .set ETX, 0x03
  57. .set EOT, 0x04
  58. # Restated from tests/exec/bootcom.s's layout block ON PURPOSE. A harness that
  59. # asks the boot sector where its input buffer is proves only that the two agree
  60. # with each other; two independent statements of the same layout have to be
  61. # written down, and a disagreement is a loud failure rather than a program that
  62. # is fed nothing. (Same reasoning, and the same lesson, as the hard-coded
  63. # RT_SZ that run_com_tests.sh used to carry: the descriptor has to be where the
  64. # shim looks, and the only way to be sure is to have written both down and
  65. # checked.)
  66. .set INLEN, 0x2000 # word: how many input bytes follow
  67. .set INCUR, 0x2002 # word: index into INBUF, NOT an address
  68. .set INBUF, 0x2004 # the bytes themselves
  69. # The case record, as rt_exec.py writes it. These offsets are the contract; the
  70. # two places that read them (this file, and case_bytes() over there) are
  71. # separate statements of it on purpose, and the cases only pass if both are
  72. # right. SLOT0 is the one that was wrong the first time round.
  73. .set OFF_DUMPLEN, 0 # word how many bytes to report
  74. .set OFF_DUMPADR, 2 # word image offset to report them from
  75. .set OFF_INLEN, 4 # byte bytes of input to hand the shim
  76. .set OFF_INBUF, 5 # byte inbuf[8]
  77. .set OFF_CASE, 13 # byte the index printed in the header
  78. .set OFF_NCALLS, 14 # word how many call slots follow
  79. .set SLOT0, 16 # call slots, 12 bytes each:
  80. # +0 word entry, +2 word arg A,
  81. # +4 byte flags, +5 pad,
  82. # +6 word arg B, +8 word CX,
  83. # +10 pad
  84. .set SLOT_SZ, 12
  85. .set CASE_SZ, 64 # 16 + 4 slots
  86. # The image layout. RT_BASE is where the runtime blob goes; everything else is
  87. # above 0x0800 so that a runtime which grew by a few hundred bytes would not
  88. # silently run into the test scaffolding. rt_exec.py asserts
  89. # RT_BASE + blob size <= HDR, so the bound is a checked one.
  90. .set RT_BASE, 0x0190
  91. .set HDR, 0x0A00
  92. .set DATA, 0x0A10
  93. .set DLEN, 32 # bytes of data area for initmem to clear
  94. .set STORE, 0x0A40 # where a read entry's result is stored
  95. .set WSTR, 0x0A50 # a string for the wrstr case: length + chars
  96. .set WSTR_EMPTY, 0x0A58 # the same, with a length of 0
  97. .set CASE, 0x0A60 # the case record
  98. .set D_DUMPLEN, 0x0AA0 # the driver's own three words
  99. .set D_DUMPADR, 0x0AA2
  100. .set D_CNT, 0x0AA4
  101. .set D_AX, 0x0AA6 # last call's AX, for the comparison cases
  102. .set RDSTR_DST, 0x0AB0 # destination for the rdstr cases
  103. .set SST_HELLO, 0x0AC0 # a short source string for strstore
  104. .set SST_LONG, 0x0AD0 # a source longer than a small max
  105. .set SST_EMPTY, 0x0AE0 # an empty source string
  106. .set SST_DST, 0x0AF0 # destination for the strstore cases
  107. .set SST_CAT, 0x0B00 # destination for the strcat cases, with a
  108. # length to append to -- unlike SST_DST,
  109. # which strstore overwrites
  110. .set SST_CAT0, 0x0B10 # ...and one that starts empty
  111. .set SST_WORLD, 0x0B20 # a source distinct from SST_HELLO
  112. .set R_BLK1, 0x0B30
  113. .set R_BLK2, 0x0B40
  114. .set R_BLK3, 0x0B50
  115. .set R_BLK4, 0x0B60
  116. .set R_BLK5, 0x0B70
  117. .set R_BLK6, 0x0B80
  118. .set R_BLK7, 0x0B90
  119. .set R_BLK8, 0x0BA0
  120. .set R_BLK9, 0x0BB0
  121. .set R_BLK10, 0x0BC0
  122. .set R_BLK11, 0x0BD0
  123. .set R_BLK12, 0x0BE0
  124. .set R_BLK13, 0x0BF0
  125. .set R_BLK14, 0x0C00
  126. .set R_BLK15, 0x0C10
  127. .set R_BLK16, 0x0C20
  128. .set R_BLK17, 0x0C30
  129. .set R_BLK18, 0x0C40
  130. .set R_BLK19, 0x0C50
  131. .set R_BLK20, 0x0C60
  132. .set R_BLK21, 0x0C70
  133. .set R_BLK22, 0x0C80
  134. .set R_BLK23, 0x0C90
  135. .set R_BLK24, 0x0CA0
  136. .set R_BLK25, 0x0CB0
  137. .set R_BLK26, 0x0CC0
  138. .set R_BLK27, 0x0CD0
  139. .set R_BLK28, 0x0CE0
  140. .set R_BLK29, 0x0CF0
  141. .set R_BLK30, 0x0D00
  142. .set R_BLK31, 0x0D10
  143. .code16
  144. .text
  145. .globl _start
  146. # The entry JMP -- the same three bytes every image this compiler writes opens
  147. # with. bootcom.s jumps to 0000:0100, which is this instruction. It is
  148. # written as raw bytes rather than `jmp drive' because gas would shorten that to
  149. # a two-byte EB 00 short jump, and the byte it dropped would then be executed as
  150. # the first instruction of the driver.
  151. _start:
  152. .byte 0xE9 # JMP rel16
  153. .word drive - _start - 3
  154. # --------------------------------------------------------------- the driver
  155. drive:
  156. cld
  157. # 1. Poison the two places a check is allowed to look. STORE gets EEEH so
  158. # a read entry that stores nothing is visible as such, and the data
  159. # area gets AAH so initmem has something to clear. The storage below
  160. # is zeroed; the poison is what turns a zero into a RESULT.
  161. movw $STORE + LOAD_BIAS, %si
  162. movw $0xEEEE, %ax
  163. movw %ax, (%si)
  164. movw $DATA + LOAD_BIAS, %si
  165. movw $DLEN, %cx
  166. movb $0xAA, %al
  167. .Lpoison:
  168. movb %al, (%si)
  169. incw %si
  170. loop .Lpoison
  171. # BP is the case-record cursor, and it is the only register that survives a
  172. # call into the runtime, so everything the loop needs is read through it
  173. # or parked in the driver's own words first.
  174. movw $CASE + LOAD_BIAS, %bp
  175. # 2. Open the record: SOH, the case index as two hex digits, STX.
  176. movb $SOH, %al
  177. call putc
  178. movb OFF_CASE(%bp), %al
  179. call hexb
  180. movb $STX, %al
  181. call putc
  182. # 3. Arm the input. INCUR is an INDEX into INBUF, and zeroing it is what
  183. # makes this boot's input start at its first byte. CX is cleared
  184. # first: see the note at the top about the high byte of CX.
  185. xorw %cx, %cx
  186. movb OFF_INLEN(%bp), %cl
  187. movw %cx, INLEN
  188. movw $0, INCUR
  189. movw $CASE + OFF_INBUF + LOAD_BIAS, %si
  190. movw $INBUF, %di
  191. xorw %ax, %ax
  192. repe cmpsb
  193. # 4. The calls. dumplen and the report address are needed after the loop,
  194. # by which time BP has moved, so they are parked now; so is the count,
  195. # because CX is gone by the second iteration.
  196. movw OFF_DUMPLEN(%bp), %ax
  197. movw %ax, D_DUMPLEN + LOAD_BIAS
  198. movw OFF_DUMPADR(%bp), %ax
  199. movw %ax, D_DUMPADR + LOAD_BIAS
  200. movw OFF_NCALLS(%bp), %ax
  201. movw %ax, D_CNT + LOAD_BIAS
  202. addw $SLOT0, %bp # BP now points AT the first call slot
  203. .Lcall:
  204. decw D_CNT + LOAD_BIAS # 1 -> 0 has SF clear, so the body runs once;
  205. jns .Lgo # 0 -> FFFF is what ends the loop
  206. jmp .Lcalldone
  207. .Lgo:
  208. call doslot
  209. addw $SLOT_SZ, %bp
  210. jmp .Lcall
  211. .Lcalldone:
  212. # 5. Close the output half and report the memory. A store that never
  213. # happened is the EEEH left in step 1, which is why it is there.
  214. movb $ETX, %al
  215. call putc
  216. movw D_DUMPLEN + LOAD_BIAS, %cx
  217. jcxz .Lnodump
  218. movw D_DUMPADR + LOAD_BIAS, %si
  219. addw $LOAD_BIAS, %si
  220. .Ldump:
  221. lodsb
  222. call hexb
  223. loop .Ldump
  224. .Lnodump:
  225. movb $EOT, %al
  226. call putc
  227. # 6. Exit. Reaching here IS the assertion that every entry returned: an
  228. # entry that hung produces no EOT, and the harness says so.
  229. movw $0x4C00, %ax # INT 21h AH=4Ch, AL=0
  230. int $0x21
  231. # doslot: call the entry described by the 12 bytes at BP, with the same
  232. # conventions Compiler.IoCall uses -- 16-bit argument(s) on the stack, popped
  233. # by the caller -- except for initmem, which takes the header in AX.
  234. #
  235. # Flags (the byte at +4):
  236. # 1 F_STACK push the word at +2; the caller pops one word.
  237. # 2 F_AX take the word at +2 in AX instead.
  238. # 4 F_INL the argument follows the call, not on the stack.
  239. # 8 F_STACK2 push the word at +6 FIRST and the word at +2 second, so +2
  240. # lands at [BP+4] and +6 at [BP+6]; the caller pops two words.
  241. # strstore wants exactly that: dest at [BP+4], source at [BP+6].
  242. # 16 F_CX load CX from the word at +8 before the call. rdstr and
  243. # strstore both take their maximum length in CX, a register the
  244. # record has no other way to reach, and neither may borrow it
  245. # from whatever the previous case left behind.
  246. #
  247. # The flags are re-read after the call because the entry has just used AX.
  248. doslot:
  249. movb 4(%bp), %al
  250. testb $8, %al
  251. jnz .Ltwostack
  252. testb $1, %al
  253. jz .Lnostack
  254. pushw 2(%bp)
  255. jmp .Lnostack
  256. .Ltwostack:
  257. pushw 6(%bp) # arg B: the source, ends at [BP+6]
  258. pushw 2(%bp) # arg A: the destination, ends at [BP+4]
  259. .Lnostack:
  260. testb $2, %al
  261. jz .Lnoax
  262. movw 2(%bp), %ax
  263. .Lnoax:
  264. testb $16, %al
  265. jz .Lnocx
  266. movw 8(%bp), %cx
  267. .Lnocx:
  268. movw (%bp), %si # image-absolute, as RT_Entry reports it
  269. addw $LOAD_BIAS, %si # so the load bias is added here, not baked in
  270. testb $4, %al
  271. jnz .Linl
  272. call *%si
  273. movw %ax, D_AX + LOAD_BIAS # observable return value
  274. movb 4(%bp), %al
  275. testb $8, %al
  276. jnz .Lcleantwo
  277. testb $1, %al
  278. jz .Lnoclean
  279. addw $2, %sp # caller-cleaned
  280. .Lnoclean:
  281. ret
  282. .Lcleantwo:
  283. addw $4, %sp
  284. ret
  285. # wrtinl's argument is NOT a stack word: the entry reads a length byte and that
  286. # many characters from its own return address. That is the caller's half of
  287. # the contract, so it is written literally here -- the length byte and the
  288. # characters must be the bytes immediately after the call, which is why this is
  289. # a second copy of the instruction rather than a jump. The compiler's own
  290. # placement of the same three things is what the string-literal fixtures in
  291. # run_com_exec.py check; duplicating a variable one here would be a second
  292. # thing to keep right, and a check that cannot fail is not a check.
  293. .Linl:
  294. call *%si
  295. .byte 5
  296. .byte 'h', 'e', 'l', 'l', 'o'
  297. ret
  298. # putc: AL to the serial port. No status polling -- qemu's 16550 always accepts,
  299. # and a poll that never went ready would hang the harness instead of failing it.
  300. putc:
  301. pushw %ax
  302. movw $0x03f8, %dx
  303. outb %al, %dx
  304. popw %ax
  305. ret
  306. # hexb: AL as two uppercase hex digits, high nibble first.
  307. #
  308. # The 8086 has no `SHR r/m16, imm8' and no shift-by-four, so the high nibble
  309. # comes from four one-bit shifts through CL. Both facts have to be written
  310. # down: gas assembles `shr $4, %ah' without complaint and rejects `shrw $1, %ah'
  311. # outright, so the only thing standing between this file and an image that
  312. # assembles cleanly and dies on a real 8086 is refusing the modern encodings.
  313. hexb:
  314. pushw %ax
  315. pushw %cx
  316. movb %al, %ah
  317. movb $1, %cl
  318. shrw %cl, %ax
  319. shrw %cl, %ax
  320. shrw %cl, %ax
  321. shrw %cl, %ax
  322. popw %cx
  323. call putdig
  324. popw %ax
  325. andb $0x0f, %al
  326. movb %al, %ah
  327. call putdig
  328. ret
  329. # putdig: the nibble in AH as one character, moved into AL so that setting AH for
  330. # INT 21h AH=02h cannot overwrite the digit it is about to print.
  331. putdig:
  332. movb %ah, %al
  333. cmpb $10, %al
  334. jb .Ldigit
  335. addb $0x37, %al # 0Ah + 37h = 'A'
  336. jmp .Lemit
  337. .Ldigit:
  338. addb $0x30, %al # '0'
  339. .Lemit:
  340. movb $2, %ah
  341. int $0x21
  342. ret
  343. # --------------------------------------------------------------- the image
  344. # The runtime blob, from tests/RtProbe.mod built at RT_BASE. The gap is the
  345. # checked bound: rt_exec.py asserts that 0x0180..RT_BASE is still all zeros, so
  346. # a driver that outgrew its space fails the suite instead of overwriting the
  347. # library it is testing.
  348. .org RT_BASE
  349. .incbin "rtblob.bin"
  350. # The program header initmem is handed: +0 flag, +2 code, +4 data base, +6 data
  351. # end. Built HERE, from the same DATA and DLEN the poison loop uses, because
  352. # the compiler's own header layout is a separate claim with its own check and
  353. # this harness is not that check. The two fields initmem READS are memory
  354. # addresses, so they carry the bias; the caller passes AX the header's memory
  355. # address too, which is the one thing rt_exec.py gets right only by saying so
  356. # here and in case_bytes().
  357. .org HDR
  358. HDR:
  359. .word 1 # hdrFlag
  360. .word 0 # hdrCS, unused by initmem
  361. .word DATA + LOAD_BIAS # hdrDS = data base
  362. .word DATA + DLEN + LOAD_BIAS # hdrHeap = data end
  363. .org DATA
  364. .zero DLEN # poisoned to AAH by the driver
  365. .org STORE
  366. .zero 2 # poisoned to EEEH by the driver
  367. # A string for the wrstr case: a length byte and that many characters, which is
  368. # the layout a compiled `string [n]' variable has. It sits above the poisoned
  369. # area so the poison never touches it, and rt_exec.py restates its address - the
  370. # two must agree or the case reads whatever bytes are there and prints them.
  371. .org WSTR
  372. .byte 5
  373. .byte 'h', 'e', 'l', 'l', 'o'
  374. .org WSTR_EMPTY
  375. .byte 0 # a length of 0: an empty string
  376. .org CASE
  377. .incbin "rtcase.bin"
  378. .org D_DUMPLEN
  379. .zero 6 # D_DUMPLEN, D_DUMPADR, D_CNT
  380. # --- strings for the string entries ----------------------------------------
  381. # A compiled `string [n]' variable is a length byte and n characters, and that
  382. # is the layout rdstr writes and strstore reads. Each DESTINATION is
  383. # pre-filled with 0xEE so a byte the entry does NOT write is visibly still
  384. # there -- that is how "the tail past the new length is left untouched" becomes
  385. # a checked claim rather than a comment. The sources are distinct addresses so
  386. # one image can offer a short string, one longer than a small max, and an empty
  387. # one, without any case depending on a previous case's leftovers.
  388. .org RDSTR_DST
  389. .fill 16, 1, 0xEE
  390. .org SST_HELLO
  391. .byte 5, 'h', 'e', 'l', 'l', 'o'
  392. .org SST_LONG
  393. .byte 8, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'
  394. .org SST_EMPTY
  395. .byte 0 # a length of 0: an empty string
  396. .org SST_DST
  397. .fill 16, 1, 0xEE
  398. # strcat reads the DESTINATION's length, so its destinations are seeded with a
  399. # real string rather than 0xEE -- and 0xEE would in fact be a 238-character
  400. # length, which is exactly the "string too long" error the entry has to raise.
  401. # SST_CAT holds `hello', SST_CAT0 is empty, and SST_WORLD is a source that is
  402. # not SST_HELLO so an append is visible as an append and not as a coincidence.
  403. .org SST_CAT
  404. .byte 5, 'h', 'e', 'l', 'l', 'o'
  405. .fill 10, 1, 0xEE
  406. .org SST_CAT0
  407. .byte 0 # a length of 0: nothing to append to
  408. .fill 15, 1, 0xEE
  409. .org SST_WORLD
  410. .byte 5, 'w', 'o', 'r', 'l', 'd'
  411. # 12-byte MBF blocks for real_add/real_sub: a at +0, b at +6.
  412. .org R_BLK1
  413. .byte 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00
  414. .org R_BLK2
  415. .byte 0x82, 0x00, 0x00, 0x00, 0x00, 0x40, 0x83, 0x00, 0x00, 0x00, 0x00, 0x20
  416. .org R_BLK3
  417. .byte 0x83, 0x00, 0x00, 0x00, 0x00, 0x20, 0x82, 0x00, 0x00, 0x00, 0x00, 0x40
  418. .org R_BLK4
  419. .byte 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00
  420. .org R_BLK5
  421. .byte 0x8A, 0x00, 0x00, 0x00, 0x00, 0x7A, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00
  422. .org R_BLK6
  423. .byte 0x82, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00
  424. .org R_BLK7
  425. .byte 0x86, 0x00, 0x00, 0x00, 0x00, 0x28, 0x8A, 0x00, 0x00, 0x00, 0x00, 0x7A
  426. .org R_BLK8
  427. .byte 0x8F, 0x00, 0x00, 0x00, 0xFE, 0x7F, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00
  428. .org R_BLK9
  429. .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x00, 0x20
  430. .org R_BLK10
  431. .byte 0x82, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x82, 0x00, 0x00, 0x00, 0x00, 0x80
  432. # Division pairs for real_div.
  433. .org R_BLK11
  434. .byte 0x83, 0x00, 0x00, 0x00, 0x00, 0x40, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00
  435. .org R_BLK12
  436. .byte 0x84, 0x00, 0x00, 0x00, 0x00, 0x20, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00
  437. .org R_BLK13
  438. .byte 0x84, 0x00, 0x00, 0x00, 0x00, 0x70, 0x82, 0x00, 0x00, 0x00, 0x00, 0x40
  439. .org R_BLK14
  440. .byte 0x8A, 0x00, 0x00, 0x00, 0x00, 0x7A, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00
  441. .org R_BLK15
  442. .byte 0x83, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00
  443. .org R_BLK16
  444. .byte 0x83, 0x00, 0x00, 0x00, 0x00, 0x40, 0x82, 0x00, 0x00, 0x00, 0x00, 0x80
  445. .org R_BLK17
  446. .byte 0x83, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x82, 0x00, 0x00, 0x00, 0x00, 0x80
  447. .org R_BLK18
  448. .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x00, 0x20
  449. .org R_BLK19
  450. .byte 0x83, 0x00, 0x00, 0x00, 0x00, 0x20, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00
  451. .org R_BLK20
  452. .byte 0x86, 0x00, 0x00, 0x00, 0x00, 0x28, 0x83, 0x00, 0x00, 0x00, 0x00, 0x40
  453. # Fractional blocks: real_int truncates `a'; real_cmp compares the pair.
  454. .org R_BLK21
  455. .byte 0x82, 0xCD, 0xCC, 0xCC, 0xCC, 0x6C, 0x82, 0xCD, 0xCC, 0xCC, 0xCC, 0x6C
  456. .org R_BLK22
  457. .byte 0x82, 0x9A, 0x99, 0x99, 0x99, 0x79, 0x82, 0xCD, 0xCC, 0xCC, 0xCC, 0x6C
  458. .org R_BLK23
  459. .byte 0x82, 0xCD, 0xCC, 0xCC, 0xCC, 0xEC, 0x82, 0x9A, 0x99, 0x99, 0x99, 0xF9
  460. .org R_BLK24
  461. .byte 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00
  462. .org R_BLK25
  463. .byte 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00
  464. .org R_BLK26
  465. .byte 0x8E, 0x00, 0x00, 0x00, 0xE7, 0x40, 0x8E, 0x00, 0x00, 0x00, 0xE6, 0x40
  466. .org R_BLK27
  467. .byte 0x82, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
  468. .org R_BLK28
  469. .byte 0x82, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
  470. .org R_BLK29
  471. .byte 0x80, 0x0A, 0xD7, 0xA3, 0x70, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
  472. .org R_BLK30
  473. .byte 0x81, 0x33, 0x33, 0x33, 0x33, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
  474. # 2^40: above reint's 0A8h early return, so it must come back unchanged.
  475. .org R_BLK31
  476. .byte 0xA9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00