boot.s 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  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 = 0xB7
  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 ss, ax
  38. ! Defer setting es until after probing the drive.
  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. ! This might clobber es.
  51. push dx
  52. mov ax, 0x0800 ! service number
  53. int 0x13
  54. mov ax, cs ! restore es
  55. mov es, ax
  56. pop ax
  57. jc cant_boot
  58. ! At this point:
  59. ! al: current drive
  60. ! cl: maximum sector number (bottom six bits)
  61. ! dh: maximum head number
  62. ! We don't care about the rest.
  63. andb cl, 0x3F
  64. ! We now need to go through a loop loading each sector in turn.
  65. ! During this loop, the registers will be set up as follows:
  66. ! al: current cylinder
  67. ! ah: maximum head
  68. ! bx: address
  69. ! cl: current sector (one based)
  70. ! ch: maximum sector (one based)
  71. ! dl: current drive
  72. ! dh: current head
  73. ! Why, yes, they are painstakingly shoehorned in to get all the data
  74. ! into registers.
  75. movb dl, al
  76. movb ch, cl
  77. movb ah, dh
  78. movb al, 0 ! start on cylinder 0
  79. mov bx, 0x0200 ! don't overwrite boot sector
  80. movb cl, 2 ! start on sector 2 (skip boot sector)
  81. movb dh, 0 ! start on head 0
  82. 1:
  83. call read_sector
  84. ! Next memory area.
  85. add bx, 0x0200
  86. cmp bx, enddata
  87. ja finished
  88. ! Next sector.
  89. incb cl
  90. cmpb cl, ch
  91. jle 1b
  92. movb cl, 1 ! back to sector 1 again
  93. ! Next head.
  94. incb dh
  95. cmpb dh, ah
  96. jle 1b
  97. movb dh, 0 ! back to head 1 again
  98. ! Next cylinder.
  99. incb al
  100. jmp 1b
  101. cant_boot:
  102. mov si, bootfail_msg
  103. call write_string
  104. jmp EXIT
  105. ! Reads a sector into memory. The parameters are:
  106. ! al: cylinder
  107. ! bx: address
  108. ! cl: sector
  109. ! dl: drive
  110. ! dh: head
  111. ! If an error occurs, it'll automatically try again. And again.
  112. ! And again...
  113. read_sector:
  114. push ax
  115. push bx
  116. push cx
  117. push dx
  118. #if 0
  119. push dx
  120. xorb dh, dh
  121. movb dl, cl
  122. call write_hex4
  123. pop dx
  124. PRINT(0x20)
  125. push dx
  126. movb dl, dh
  127. xorb dh, dh
  128. call write_hex4
  129. pop dx
  130. PRINT(0x20)
  131. push dx
  132. movb dl, al
  133. xorb dh, dh
  134. call write_hex4
  135. pop dx
  136. #endif
  137. 1:
  138. movb ch, al
  139. mov ax, 0x0201 ! service 2, read one sector
  140. int 0x13
  141. jc 2f
  142. mov ax, 0x0E2E ! write out a .
  143. mov bx, 0x0007 ! page 0, white
  144. int 0x10
  145. pop dx
  146. pop cx
  147. pop bx
  148. pop ax
  149. ret
  150. ! If a read fail occurs, the spec (such as it is) states that we need
  151. ! to reset the fd controller and try again.
  152. 2:
  153. push ax
  154. push bx
  155. mov ax, 0x0E21 ! write out a !
  156. mov bx, 0x0007 ! page 0, white
  157. int 0x10
  158. mov ax, 0x0000
  159. int 0x13
  160. pop bx
  161. pop ax
  162. jmp 1b
  163. ! Waits for a keystroke (and then discards it).
  164. pause:
  165. push ax
  166. xorb ah, ah
  167. int 0x16
  168. pop ax
  169. ret
  170. ! This utility writes the string pointed to by ds:si out to the console.
  171. write_string:
  172. push ax
  173. push bx
  174. 1:
  175. lodsb
  176. andb al, al
  177. jz 2f
  178. movb ah, 0xE ! service
  179. mov bx, 0x0007 ! page 0, white
  180. int 0x10
  181. jmp 1b
  182. 2:
  183. pop bx
  184. pop ax
  185. ret
  186. ! Writes out the contents of dx as hex.
  187. write_hex4:
  188. push ax
  189. push cx
  190. mov cx, 4 ! 4 hex digits
  191. 1:
  192. rol dx, 1 ! rotate so that highest 4 bits are at the bottom
  193. rol dx, 1
  194. rol dx, 1
  195. rol dx, 1
  196. mov ax, 0xE0F ! ah = request, al = mask for nybble
  197. andb al, dl
  198. addb al, 0x90 ! convert al to ascii hex (four instructions)
  199. daa
  200. adcb al, 0x40
  201. daa
  202. int 0x10
  203. loop 1b
  204. pop cx
  205. pop ax
  206. ret
  207. ! Everything loaded successfully!
  208. !
  209. ! We now need to do some setup and start the program itself.
  210. finished:
  211. mov si, running_msg
  212. call write_string
  213. ! Wipe the bss. (I'm a little suprised that __m_a_i_n doesn't do this.)
  214. mov di, begbss
  215. mov cx, endbss
  216. sub cx, di
  217. mov ax, 0
  218. rep stosb
  219. ! Push standard parameters onto the stack and go.
  220. push envp ! envp
  221. push argv ! argv
  222. push 1 ! argc
  223. call __m_a_i_n
  224. ! fall through into the exit routine.
  225. ! Halts, waits for a keypress, and reboots. This also becomes the
  226. ! application termination routine.
  227. .define __exit
  228. .extern __exit
  229. .define EXIT
  230. .extern EXIT
  231. __exit:
  232. EXIT:
  233. mov si, halted_msg
  234. call write_string
  235. 1:
  236. jmp 1b
  237. xor ax, ax
  238. int 0x16 ! get key
  239. int 0x19 ! reboot
  240. ! Some text messages.
  241. banner_msg: .asciz 'ACKBOOT\n\r'
  242. nl_msg = banner_msg + 7 ! cheap trick
  243. bootfail_msg: .asciz 'Unable to boot!\n\r'
  244. loading_msg: .asciz '\n\rLoading...\n\r'
  245. halted_msg: .asciz '\n\rHalted.\n\r'
  246. running_msg: .asciz '\n\rRunning.\n\r'
  247. ! The argv and env arrays.
  248. argv: .data2 exename, 0
  249. envp: .data2 0
  250. exename: .asciz 'pc86.img'
  251. ! ...and we need this to fool the PC into booting our boot sector.
  252. .space PADDING
  253. .data2 0xAA55
  254. ! Define symbols at the beginning of our various segments, so that we can find
  255. ! them. (Except .text, which has already been done.)
  256. .define begtext, begdata, begbss
  257. .sect .data; begdata:
  258. .sect .rom; begrom:
  259. .sect .bss; begbss:
  260. ! Some magic data. All EM systems need these.
  261. .define .trppc, .ignmask, _errno
  262. .comm .trppc, 4
  263. .comm .ignmask, 4
  264. .comm _errno, 4