boot.s 5.8 KB

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