Advertisement
jcomeau_ictx

boot.asm before changing relocation code broken on qemu

Dec 29th, 2015
139
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. .intel_syntax noprefix ;# floppy boot segment
  2. ;# 0x000-0x400 is BIOS interrupt table
  3. ;# 0x400-0x500 is BIOS system information area
  4. ;# we can use starting at 0x500
  5.  
  6. .equ upper_right, 158 ;# address of upper-right corner of screen
  7. .ifdef LOADBLOCKS
  8.  ;# this 'ifdef' is only here to trigger the Makefile to include this number
  9.  ;# but let's use it to define other constants while we're at it
  10.  .equ HEADS, 2
  11.  .equ SECTORS_PER_TRACK, 18
  12.  .equ BYTES_PER_CYLINDER, 512 * HEADS * SECTORS_PER_TRACK
  13.  .equ LOAD_CYLINDERS, (LOADBLOCKS * 1024) / BYTES_PER_CYLINDER
  14.  .if (LOADBLOCKS * 1024) % BYTES_PER_CYLINDER > 0
  15.   .equ LOAD_CYLINDERS, LOAD_CYLINDERS + 1
  16.  .endif
  17. .endif
  18. .macro showprogress
  19.   .ifdef DEBUGGING
  20.   call progress
  21.   .endif
  22. .endm
  23. .macro shownumber
  24.  .ifdef DEBUGGING
  25.  call shownumber
  26.  .endif
  27. .endm
  28.  
  29. .org 0 ;# actually 0x7c00, where the BIOS loads the bootsector
  30. start:
  31.     jmp  start0
  32. bootmode: ;# this byte after the short jmp above is normally just padding
  33. ;# we will use it to indicate a startup from MSDOS
  34. ;# also to indicate El Torito boot
  35. .ifdef EL_TORITO_BOOT
  36.     .byte 0xfe ;# even number so we can use MSDOS check odd parity
  37. .else
  38.     .byte 0 ;# pad to offset 3
  39. .endif
  40.     .ascii "cmcf 1.0"
  41. bps:
  42.     .word 512     ;# bytes/sector
  43.     .byte 1       ;# sector/cluster
  44.     .word 1       ;# sector reserved
  45.     .byte 2       ;# fats
  46.     .word 16*14   ;# root directory entries
  47.     .word 80 * HEADS * SECTORS_PER_TRACK ;# sectors
  48.     .byte 0x0f0   ;# media
  49.     .word 9       ;# sectors/fat
  50. spt:
  51.     .word SECTORS_PER_TRACK ;# sectors/track
  52. heads:
  53.     .word HEADS   ;# heads
  54.     .long 0       ;# hidden sectors
  55.     .long 80 * HEADS * SECTORS_PER_TRACK ;# sectors again
  56. drive:
  57.     .byte 0       ;# drive
  58. cylinder: .word 0
  59. .align 4
  60. ;# forth+icons+blocks 24-LOADBLOCKS ;# number of cylinders to load (out of 80)
  61. ;# needs to be on longword boundary, see nc_ in color.asm
  62. nc:  .long LOAD_CYLINDERS
  63. gdt: .word gdt_end - gdt0 - 1 ;# GDT limit
  64.     .long gdt0 + loadaddr ;# pointer to start of table
  65. .align 8
  66. gdt0: .word 0, 0, 0, 0 ;# start of table must be a null entry
  67.     .equ code32p, . - gdt0
  68.     .word 0xffff, 0, 0x9a00, 0xcf ;# 32-bit protected-mode code
  69.     .equ data32p, . - gdt0
  70.     .word 0xffff, 0, 0x9200, 0xcf ;# 32-bit protected-mode data
  71.     .equ code16r, . - gdt0
  72.     .word 0xffff, 0, 0x9a00, 0x00 ;# 16-bit real-mode code
  73.     .equ data16r, . - gdt0
  74.     .word 0xffff, 0, 0x9200, 0x00 ;# 16-bit real-mode data
  75. gdt_end:
  76. real_mode_idt:
  77.     .word 0xffff ;# limit
  78.     .long 0 ;# base is that of BIOS interrupt table
  79. .code16
  80. start0:
  81.     cmp  word ptr cs:0, 0x20cd ;# INT 20h at start of .com program header
  82.     jnz  0f ;# not a .com program, so must be bootsector
  83.     dec  byte ptr [cs: bootmode + 0x100] ;# set indicator byte
  84. 0:  cli
  85.     push cs ;# need to set DS=CS, especially on VMWare, DS was 0x40
  86.     pop  ds
  87.     zero ss ;# set stack to something low and out of the way
  88.     mov  esp, iobuffer + 0x1000
  89.     call whereami ;# set EBP to load location
  90.     call relocate ;# move past BIOS stuff and buffer space
  91.     mov  esp, gods ;# stack pointer now where it really belongs
  92.     call whereami ;# set EBP to where we relocated
  93.     showprogress
  94.     data32 call protected_mode
  95. .code32
  96.     call a20 ;# set A20 gate to enable access to addresses with 1xxxxx
  97.     mov  al, byte ptr bootmode + loadaddr
  98.     or   al, al  ;# negative if MSDOS-loaded or El Torito non-emulation mode
  99.     jz   0f ;# otherwise we need to load bootcode from floppy
  100. 9:  mov  esi, godd ;# set up data stack pointer for 'god' task
  101.     jmp  start1
  102. 0:  mov  byte ptr [es: 0xb8000 | (upper_right - 8)], 'P'
  103.     call unreal_mode
  104. .code16
  105.     ;# fall through to cold-start routine
  106. cold:
  107.     mov  edi, loadaddr  ;# start by overwriting this code
  108.     ;# that's why it's so critical that 'cylinder' be reset to zero before
  109.     ;# saving the image; if it's anything but that, when this block is
  110.     ;# overwritten, 'cylinder' will be changed, and cylinders will be skipped
  111.     ;# in loading, making the bootblock unusable
  112.     call read
  113.     inc  byte ptr cylinder + loadaddr
  114.     xor  cx, cx
  115.     mov  cl, nc + loadaddr ;# number of cylinders used
  116.     dec  cx
  117. 0:  push cx
  118.     call read
  119.     inc  byte ptr cylinder + loadaddr
  120.     mov  dx, word ptr cylinder + loadaddr
  121.     shownumber
  122.     pop  cx
  123.     loop 0b
  124.     showprogress
  125.     data32 call protected_mode
  126. .code32
  127.     jmp  9b
  128.  
  129. .code16
  130. whereami: ;# set EBP as PC-relative pointer
  131.     xor  ebp, ebp ;# clear first or this might not work
  132.     mov  eax, ebp
  133.     mov  bp, cs ;# get CS segment register
  134.     shl  ebp, 4 ;# shift segment to where it belongs in 32-bit absolute addr
  135.     pop  ax ;# get return address
  136.     push ax ;# we still need to return to it
  137.     ;# note: the following only works if this called from first 256 bytes
  138.     and  ax, 0xff00 ;# clear low 8 bits of address
  139.     add  ebp, eax ;# EBP now contains 32-bit absolute address of 'start'
  140.     ret
  141.  
  142. read:
  143. ;# about to read 0x4800, or 18432 bytes
  144. ;# total of 165888 (0x28800) bytes in 9 cylinders, 1.44 MB in 80 cylinders
  145. ;# (low 8 bits of) cylinder number is in CH, (6 bit) sector number in CL
  146.     push edx  ;# used for debugging during boot
  147.     mov  ebx, iobuffer
  148.     push ebx
  149.     mov  ax, (2 << 8) + 36  ;# 18 sectors per head, 2 heads
  150.     mov  ch, [cylinder + loadaddr]
  151.     mov  cl, 1  ;# sector number is 1-based, and we always read from first
  152.     ;#mov  dx, cx
  153.     ;#shownumber
  154.     mov  dx, 0x0000 ;# head 0, drive 0
  155.     int  0x13
  156.     mov  edx, esi  ;# temporarily store parameter stack pointer in EDX
  157.     pop  esi  ;# load iobuffer
  158.     mov  ecx, 512*18*2/4
  159.     ;#showprogress
  160.     addr32 rep movsd ;# move to ES:EDI location preloaded by caller
  161.     ;#showprogress
  162.     mov  esi, edx  ;# restore parameter stack pointer
  163.     pop  edx
  164.     ret
  165.  
  166. .ifdef DEBUGGING
  167. .code16
  168. progress: ;# show progress indicator
  169.     pop  dx  ;# grab return address
  170.     push dx  ;# and put back on stack
  171. shownumber: ;# alternate entry point, preload DX register
  172.     push ax
  173.     push bx
  174.     push cx
  175.     push 0xb800 ;# video display RAM
  176.     pop  es
  177.     mov  cx, 4  ;# four digit address to display
  178.     mov  bx, upper_right ;# corner of screen
  179. 0:  mov  al, dl ;# get number as hex digit
  180.     and  al, 0xf
  181.     add  al, (0x100 - 10) ;# will force a carry if > 9
  182.     jnc  1f
  183.     add  al, ('a' - '9' - 1)
  184. 1:  add  al, ('9' + 1)
  185.     es: mov  [bx], al ;# assumes ES=0xb800, text mode 3 video RAM
  186.     shr  dx, 4 ;# next higher hex digit
  187.     sub  bx, 2 ;# move to previous screen position
  188.     loop 0b
  189.     mov  ax, cs ;# restore segment registers
  190.     mov  es, ax
  191.     ;# restore registers
  192.     pop  cx
  193.     pop  bx
  194.     pop  ax
  195.     ret
  196. .endif
  197.  
  198. relocate:  ;# move code from where DOS or BIOS put it, to where we want it
  199. ;# it would seem to be trivial, but in cases where the 64K span of source
  200. ;# and destination overlap, we overwrite this relocation code while it is
  201. ;# running, and crash. it happened.
  202. ;# one way to avoid it is to move everything to end of 640K base RAM
  203. ;# and then move it back to its final destination.
  204. ;# but that is inefficient. so instead we just move the relocation code to
  205. ;# the stack, which should be out of the way.
  206.     cmp  byte ptr bootmode + loadaddr, 0xfe ;# already in correct place?
  207.     jz   9f  ;# if so, skip the difficult stuff
  208.     mov  ax, (0xa0000 - 0x10000) >> 4 ;# segment address of relocation
  209.     mov  es, ax
  210.     mov  eax, ebp ;# get source address
  211.     shr  eax, 4
  212.     mov  ds, eax
  213.     mov  cx, 0x10000 >> 1 ;# moving 64K in words
  214.     mov  si, 0xfffe
  215.     mov  di, si
  216.     push cx
  217.     std  ;# move words from 0xfffe to 0
  218.     rep  movsw
  219. ;# now we need to do a tricky jump from here to where the relocated code is,
  220. ;# otherwise we still run the risk of a code overwrite.
  221.     mov  ax, offset (5f-start)
  222.     push es
  223.     push ax
  224.     lret ;# "return" to following address, in the relocated segment
  225. 5:  pop  cx
  226.     xor  si, si ;# now move from 0
  227.     mov  di, si
  228.     push es ;# destination now becomes source
  229.     pop  ds
  230.     mov  ax, loadaddr >> 4
  231.     mov  es, ax
  232.     cld
  233.     rep  movsw
  234. 9:  xor  ax, ax ;# now all segments are 0
  235.     mov  ds, ax
  236.     mov  es, ax
  237.     pop  ax ;# get return address, but we want to "ret" to its new address
  238.     and  ax, 0xff ;# this routine must be called from first 256 bytes!
  239.     add  ax, loadaddr
  240.     push 0 ;# return with CS=0
  241.     push ax
  242.     lret
  243.  
  244. protected_mode:
  245.     cli  ;# we're not set up to handle interrupts in protected mode
  246.     lgdt [gdt + loadaddr]
  247.     mov  eax, cr0
  248.     or   al, 1
  249.     mov  cr0, eax
  250.     jmp  code32p: offset pm + loadaddr
  251. .code32
  252. pm: mov  ax, data32p ;# set all segment registers to protected-mode selector
  253.     mov  ds, ax
  254.     mov  es, ax
  255.     mov  ss, ax  ;# same base as before (0), or ret wouldn't work!
  256.     ret  ;# now it's a 32-bit ret; no "ret far" needed
  257.  
  258. unreal_mode:
  259.     jmp  code16r: offset code16p + loadaddr
  260. .code16
  261. code16p: ;# that far jump put us in 16-bit protected mode
  262. ;# now let's put at least the stack segment back to 16 bits
  263.     mov  ax, data16r
  264. ;# any segments left commented out will be in "unreal" mode
  265.     mov  ss, ax
  266.     ;#mov  ds, ax
  267.     ;#mov  es, ax
  268.     mov  eax, cr0
  269.     and  al, 0xfe ;# mask out bit 0, the PE (protected-mode enabled) bit
  270.     mov  cr0, eax
  271.     jmp  0:offset unreal + loadaddr
  272. unreal:  ;# that far jump put us back to a realmode CS
  273.     xor  ax, ax
  274.     mov  ss, ax
  275.     mov  ds, ax
  276.     mov  es, ax
  277.     lidt [real_mode_idt + loadaddr]
  278.     sti  ;# re-enable interrupts
  279.     data32 ret ;# adjust stack appropriately for call from protected mode
  280.  
  281. a20:
  282.     mov  al, 0x0d1
  283.     out  0x64, al ;# to keyboard
  284. 0:  in   al, 0x64
  285.     and  al, 2
  286.     jnz  0b
  287.     mov  al, 0x4b
  288.     out  0x60, al ;# to keyboard, enable A20
  289.     ret
  290.  
  291. ;# don't need 'write' till after bootup
  292.     .org 0x1fe + start
  293.     .word 0x0aa55 ;# mark boot sector
  294.     ;# end of boot sector
  295.  
  296. write:
  297.     mov  edi, iobuffer ;# destination address
  298.     mov  ebx, edi  ;# save in EBX for BIOS call
  299.     mov  ecx, 512*18*2/4 ;# using 32-bit MOVS instruction
  300.     addr32 rep movsd
  301.     mov  ax, (3 << 8) + 36  ;# 18 sectors per head, 2 heads
  302.     mov  dx, 0x0000 ;# head 0, drive 0
  303.     mov  ch, [cylinder + loadaddr] ;# cylinder number in CH
  304.     mov  cl, 1  ;# sector number is 1-based
  305.     int  0x13  ;# let BIOS handle the nitty-gritty floppy I/O
  306.     ret
  307.  
  308. graphicsmode:
  309.     mov  ax, 0x4f01 ;# get video mode info
  310.     mov  cx, vesa ;# a 16-bit color linear video mode (5:6:5 rgb)
  311.     mov  di, iobuffer ;# use floppy buffer, not during floppy I/O!
  312.     int  0x10
  313.     mov  ax, 0x4f02 ;# set video mode
  314.     mov  bx, cx ;# vesa mode
  315.     int  0x10
  316.     mov  ebx, iobuffer + 0x28 ;# linear frame buffer address
  317.     mov  eax, [ebx]
  318.     mov  [displ + loadaddr], eax
  319.     ret
  320.  
  321. .code32 ;# these routines called from high-level Forth (protected mode)
  322. readf:
  323.     mov  cylinder + loadaddr, al
  324.     dup_ ;# save cylinder number
  325.     push edi
  326.     mov  edi, [esi+4]
  327.     shl  edi, 2  ;# convert longwords to bytes...
  328.     add  edi, loadaddr ;# ... and then to absolute address
  329.     call unreal_mode
  330. .code16
  331.     call read
  332.     data32 call protected_mode
  333. .code32
  334.     pop  edi
  335.     ;# fall through to common code
  336.  
  337. ;# common code for both reads and writes
  338. readf1:
  339.     drop ;# restore EAX, cylinder number
  340.     inc  eax  ;# next cylinder
  341.     add  dword ptr [esi], 0x1200  ;# move memory pointer up this many longwords
  342.     ret
  343.  
  344. writef:  ;# write cylinder to floppy disk
  345. ;# called with cylinder number in AL, source address, in longwords, in ESI
  346.     mov  cylinder + loadaddr, al
  347.     dup_ ;# save cylinder number
  348.     push esi  ;# save data stack pointer, we need it for memory transfer
  349.     mov  esi, [esi+4]  ;# get memory offset
  350.     shl  esi, 2  ;# convert from longwords to bytes...
  351.     add  esi, loadaddr  ;# ... and then to absolute address
  352.     call unreal_mode
  353. .code16
  354.     call write
  355.     data32 call protected_mode
  356. .code32
  357.     pop  esi  ;# restore stack pointer
  358.     jmp  readf1  ;# join common code
  359.  
  360. buffer:  ;# return IO buffer address in words, for compatibility reasons
  361.     dup_
  362.     mov  eax, iobuffer >> 2
  363.     ret
  364.  
  365. off:  ;# return loadaddr expressed in words
  366. ;# this is the offset in RAM to where block 0 is loaded
  367.     dup_
  368.     mov  eax, loadaddr >> 2
  369.     ret
  370.  
  371. setgraphics:
  372.     call unreal_mode
  373. .code16
  374.     addr32 mov byte ptr [es: 0xb8000 | (upper_right - 8)], 'T'
  375.     call graphicsmode
  376.     addr32 mov byte ptr [es: 0xb8000 | (upper_right - 8)], 'G'
  377.     data32 call protected_mode
  378. .code32
  379.     ret
  380.  
  381. ;# these must be defined elsewhere before use
  382. seekf:
  383. cmdf:
  384. readyf:
  385. stop:
  386.     ret
Advertisement
RAW Paste Data Copied
Advertisement