OS(1) IBM-PCのブートシーケンス

IPLがロードされるメモリアドレス

周知のとおり、IBM-PCの場合BIOSの初期が完了後、BIOSの設定に従って検索された起動デバイスの先頭先頭セクタがメモリに読み込まれて実行開始するわけですが、物理アドレスで言うと0x7c00になります。
ただCPUはリアルモード(リアルアドレスモード)で動作しているため、セグメント:オフセットの組となり、実際には0000:7C00からのスタートとなるとのこと。

出所: http://hem.passagen.se/danma/nboot.htm

これを確認するため(だけ)のIPLを作成してみる。

seg.asm

ソースプログラムから

  1. nasm -o seg.bin seg.asm としてIPLデータ(512バイト)を作成
  2. ruby makeFDimage.rb seg.bin seg.img としてFDイメージファイルを作成
  3. Virtual PCを起動し、ステータスバーのFDアイコンへ seg.imgをDrag&Drop

としてして実行すればよし。

なお、このプログラムは
nasm -o seg.com -DCOMPROG seg.asm
としてやれば、コマンドプロンプトで実行できるCOMファイルとなります。違いは [org 0x100]となるところと、プログラム終了のMS-DOSファンクションコールを呼び出すところ。

seg.asmソース

;
; 開始アドレス seg:offset を画面に表示
;
%ifdef	COMPROG
[org 0x100]
%endif

start:		call	next
next:		
		mov	ax, cs		; CSを表示
		call	put_AX
		mov	al, ':'
		call	putc

		pop	ax		; 開始オフセット(start)を表示
		sub	ax, next - start
		call	put_AX
		
		call	put_CRLF

%ifdef	COMPROG
		mov	ax, 0x4c00
		int	0x21
%else
		jmp	$
%endif

;========================================================================================
put_CRLF:	push	ax
		mov	al, 13
		call	putc
		mov	al, 10
		call	putc
		pop	ax
		ret

;
; Video BIOSを利用したASCIZ文字列出力
;
puts:		lodsb
		or	al, al
		jz	.@ret
		call	putc
		jmp	puts
.@ret:		ret
		

;
; 空白を出力
;
put_SPC:	push	ax
		mov	al, ' '
		call	putc
		pop	ax
		ret

;
; Video BIOSを利用した1文字出力
;
putc:		push	bx
		mov	ah, 0x0e
		mov	bx, 7		; 白
		int	0x10
		pop	bx
		ret

;
; AXレジスタを16進4桁で出力
;
put_AX:		push	ax
		push	cx
		mov	cx, 4
.@loop:		rol	ax, 4
		push	ax
		and	al, 0x0f	; 4ビットを取り出し
		add	al, '0'
		cmp	al, '9'
		jbe	.@disp
		add	al, 'A' - '9' - 1
.@disp:		call	putc
		pop	ax
		loop	.@loop
		pop	cx
		pop	ax
		ret


;
; AXレジスタを16進4桁で出力
;
put_AL:		push	ax
		push	cx
		mov	cx, 2
.@loop:		rol	al, 4
		push	ax
		and	al, 0x0f
		add	al, '0'
		cmp	al, '9'
		jbe	.@disp
		add	al, 'A' - '9' - 1
.@disp:		call	putc
		pop	ax
		loop	.@loop
		pop	cx
		pop	ax
		ret

%ifndef	COMPROG
		times 510 - ($ - $$) db 0
		dw	0xaa55
%endif

makeFDimage.rb ソース

#! ruby -Ks

if ARGV.size != 2
  puts "usage: mkfdimage in-file out-file"
  exit
end

data = ""
File.open(ARGV[0], "r+b") { |f|
  data = f.read
}

File.open(ARGV[1], "wb") { |f|
  f.write(data)
  f.truncate(80 * 2 * 18 * 512)  
}