boot.s 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. #
  2. ! $Source$
  3. ! $State$
  4. ! $Revision$
  5. ! Declare segments (the order is important).
  6. .sect .text
  7. .sect .rom
  8. .sect .data
  9. .sect .bss
  10. .sect .text
  11. ! ****** WARNING! ******
  12. !
  13. ! The PC boot sector requires a magic number at the end to signify that the
  14. ! disk is bootable. Unfortunately, the ACK assembler is a bit simple and we
  15. ! can't tell it to put the 0xAA55 at a particular address without writing our
  16. ! own custom binary generator. As a result, we need to manually insert just
  17. ! the right amount of padding in order to make this work.
  18. !
  19. ! If you ever need to change the boot code, this needs adjusting. I recommend
  20. ! a hex editor.
  21. PADDING = 0xB9
  22. ! Some definitions.
  23. BOOT_SEGMENT = 0x07C0 ! Where we've been loaded
  24. #define PRINT(N) push ax; push bx; movb ah, 0x0E; movb al, N; mov bx, 0x0007; int 0x10; pop bx; pop ax
  25. begtext:
  26. ! This code makes up the PC boot sector, and is the first thing on the
  27. ! floppy disk. The PC will load this sector to 0x07C0:0000 and jump to it
  28. ! with dl set to our drive, but it won't necessarily do it in a sane way
  29. ! (some BIOSes jump to 0x0000:7C00 instead). So, we need to fix that.
  30. jmpf BOOT_SEGMENT : start2
  31. start2:
  32. ! Set up the segment descriptors. We're running in tiny mode, so it's just
  33. ! a matter of copying the contents of cs (already been set up by the jmpf)
  34. ! into the other registers.
  35. mov ax, cs
  36. mov ds, ax
  37. mov es, ax
  38. mov ss, ax
  39. ! Initialise the stack, which will start at the top of our segment and work
  40. ! down.
  41. mov sp, 0 ! the first push will wrap round to 0xFFFF
  42. ! Some more startup housekeeping.
  43. sti
  44. cld
  45. ! We're now set up for actual code. Write out our banner. Remember that
  46. ! at this point dl contains our drive number, which we want to keep.
  47. mov si, banner_msg
  48. call write_string
  49. ! Probe the drive to figure out its geometry.
  50. push dx
  51. mov ax, 0x0800 ! service number
  52. int 0x13
  53. pop ax
  54. jc cant_boot
  55. ! At this point:
  56. ! al: current drive
  57. ! cl: maximum sector number (bottom six bits)
  58. ! dh: maximum head number
  59. ! We don't care about the rest.
  60. andb cl, 0x3F
  61. ! We now need to go through a loop loading each sector in turn.
  62. ! During this loop, the registers will be set up as follows:
  63. ! al: current cylinder
  64. ! ah: maximum head
  65. ! bx: address
  66. ! cl: current sector (one based)
  67. ! ch: maximum sector (one based)
  68. ! dl: current drive
  69. ! dh: current head
  70. ! Why, yes, they are painstakingly shoehorned in to get all the data
  71. ! into registers.
  72. movb dl, al
  73. movb ch, cl
  74. movb ah, dh
  75. movb al, 0 ! start on cylinder 0
  76. mov bx, 0x0200 ! don't overwrite boot sector
  77. movb cl, 2 ! start on sector 2 (skip boot sector)
  78. movb dh, 0 ! start on head 0
  79. 1:
  80. call read_sector
  81. ! Next memory area.
  82. add bx, 0x0200
  83. cmp bx, enddata
  84. ja finished
  85. ! Next sector.
  86. incb cl
  87. cmpb cl, ch
  88. jle 1b
  89. movb cl, 1 ! back to sector 1 again
  90. ! Next head.
  91. incb dh
  92. cmpb dh, ah
  93. jle 1b
  94. movb dh, 0 ! back to head 1 again
  95. ! Next cylinder.
  96. incb al
  97. jmp 1b
  98. cant_boot:
  99. mov si, bootfail_msg
  100. call write_string
  101. jmp EXIT
  102. ! Reads a sector into memory. The parameters are:
  103. ! al: cylinder
  104. ! bx: address
  105. ! cl: sector
  106. ! dl: drive
  107. ! dh: head
  108. ! If an error occurs, it'll automatically try again. And again.
  109. ! And again...
  110. read_sector:
  111. push ax
  112. push bx
  113. push cx
  114. push dx
  115. #if 0
  116. push dx
  117. xorb dh, dh
  118. movb dl, cl
  119. call write_hex4
  120. pop dx
  121. PRINT(0x20)
  122. push dx
  123. movb dl, dh
  124. xorb dh, dh
  125. call write_hex4
  126. pop dx
  127. PRINT(0x20)
  128. push dx
  129. movb dl, al
  130. xorb dh, dh
  131. call write_hex4
  132. pop dx
  133. #endif
  134. 1:
  135. movb ch, al
  136. mov ax, 0x0201 ! service 2, read one sector
  137. int 0x13
  138. jc 2f
  139. mov ax, 0x0E2E ! write out a .
  140. mov bx, 0x0007 ! page 0, white
  141. int 0x10
  142. pop dx
  143. pop cx
  144. pop bx
  145. pop ax
  146. ret
  147. ! If a read fail occurs, the spec (such as it is) states that we need
  148. ! to reset the fd controller and try again.
  149. 2:
  150. push ax
  151. push bx
  152. mov ax, 0x0E21 ! write out a !
  153. mov bx, 0x0007 ! page 0, white
  154. int 0x10
  155. mov ax, 0x0000
  156. int 0x13
  157. pop bx
  158. pop ax
  159. jmp 1b
  160. ! Waits for a keystroke (and then discards it).
  161. pause:
  162. push ax
  163. xorb ah, ah
  164. int 0x16
  165. pop ax
  166. ret
  167. ! This utility writes the string pointed to by ds:si out to the console.
  168. write_string:
  169. push ax
  170. push bx
  171. 1:
  172. lodsb
  173. andb al, al
  174. jz 2f
  175. movb ah, 0xE ! service
  176. mov bx, 0x0007 ! page 0, white
  177. int 0x10
  178. jmp 1b
  179. 2:
  180. pop bx
  181. pop ax
  182. ret
  183. ! Writes out the contents of dx as hex.
  184. write_hex4:
  185. push ax
  186. push cx
  187. mov cx, 4 ! 4 hex digits
  188. 1:
  189. rol dx, 1 ! rotate so that highest 4 bits are at the bottom
  190. rol dx, 1
  191. rol dx, 1
  192. rol dx, 1
  193. mov ax, 0xE0F ! ah = request, al = mask for nybble
  194. andb al, dl
  195. addb al, 0x90 ! convert al to ascii hex (four instructions)
  196. daa
  197. adcb al, 0x40
  198. daa
  199. int 0x10
  200. loop 1b
  201. pop cx
  202. pop ax
  203. ret
  204. ! Everything loaded successfully!
  205. !
  206. ! We now need to do some setup and start the program itself.
  207. finished:
  208. mov si, running_msg
  209. call write_string
  210. ! Wipe the bss. (I'm a little suprised that __m_a_i_n doesn't do this.)
  211. mov di, begbss
  212. mov cx, endbss
  213. sub cx, di
  214. mov ax, 0
  215. rep stosb
  216. ! Push standard parameters onto the stack and go.
  217. push envp ! envp
  218. push argv ! argv
  219. push 1 ! argc
  220. call __m_a_i_n
  221. ! fall through into the exit routine.
  222. ! Halts, waits for a keypress, and reboots. This also becomes the
  223. ! application termination routine.
  224. .define __exit
  225. .extern __exit
  226. .define EXIT
  227. .extern EXIT
  228. __exit:
  229. EXIT:
  230. mov si, halted_msg
  231. call write_string
  232. 1:
  233. jmp 1b
  234. xor ax, ax
  235. int 0x16 ! get key
  236. int 0x19 ! reboot
  237. ! Some text messages.
  238. banner_msg: .asciz 'ACKBOOT\n\r'
  239. nl_msg = banner_msg + 7 ! cheap trick
  240. bootfail_msg: .asciz 'Unable to boot!\n\r'
  241. loading_msg: .asciz '\n\rLoading...\n\r'
  242. halted_msg: .asciz '\n\rHalted.\n\r'
  243. running_msg: .asciz '\n\rRunning.\n\r'
  244. ! The argv and env arrays.
  245. argv: .data2 exename, 0
  246. envp: .data2 0
  247. exename: .asciz 'pc86.img'
  248. ! ...and we need this to fool the PC into booting our boot sector.
  249. .space PADDING
  250. .data2 0xAA55
  251. ! Define symbols at the beginning of our various segments, so that we can find
  252. ! them. (Except .text, which has already been done.)
  253. .define begtext, begdata, begbss
  254. .sect .data; begdata:
  255. .sect .rom; begrom:
  256. .sect .bss; begbss:
  257. ! Some magic data. All EM systems need these.
  258. .define .trppc, .ignmask, _errno
  259. .comm .trppc, 4
  260. .comm .ignmask, 4
  261. .comm _errno, 4