自作OSで文字列をPC98に表示する

高二 N.K.

はじめに

二度目の登場の高2のN.Kです。展示用に作ったものの簡単な解説をしようと思います。

実際の展示

まず文化祭で展示する物を紹介しようと思います。 画像の通りPC98でTHANK YOU FOR COMING TO APCと表示させています。

展示

基本的なプログラムの仕組み

ざっくり言うとメモリーの中のテキストVRAMに割り当てられている領域にASCII文字コードを書き込んで文字を表示しています。 一定の周期でコンピュータがテキストVRAMの内容を読み込んでモニターへ送るようになっているので、テキストVRAMに文字を書き込むと書き込んだ番地に応じた画面上の位置に文字が表示されます。

実際のコード

今回はアセンブリ言語というプログラミング言語を使用します。 https://github.com/asanobuturi/PC98APCOS で全体のコードを公開しています。 はじめはフロッピーディスクの情報を指定します。 1行目のJMP entryで文字を書くプログラムの場所を示していますJMP命令はC言語でいうところのgoto文と同じです。
2行目はNOP命令でCPUに何もしないで一命令分の実行時間を消費するものだそうです。なぜこれが必要なのかはよくわかりませんがこれが一般的だそうです。
3行目はブートセクタの名前を8byteで指定しています。ブートセクタとは起動に必要なプログラムや情報を記録したものです。
4行目は1セクタの大きさを指定しています。セクタとは円盤型の記憶媒体の最小の記録単位でフロッピーの場合は512byteなので512を指定します。
5行目はクラスタあたりのセクタ数を指定しています。
クラスタとはOSが記録媒体を管理する際の最小単位で2の累乗である必要があり、今回は1クラスタあたり1セクタとしています。
6行目は予約領域のセクタ数を指定しています。
予約領域とはPCの起動に必要なプログラムのことですなわちこのプログラム自身です。ディスクのはじめにこれがあるので1を指定しています。
7行目はFATの個数で2を指定するのが一般的だそうです。
8行目はルートディレクトリでのファイルの情報が格納されているディレクトリエントリの数をいれています。
9行目はディスクのセクタ数です。フロッピーディスクは2880セクタです。
10行目はハードディスクだと0xf8、リムーバブルメディア(電源が入っている状態でも取り付けや取り外しができるるもの ex:USBメモリ,フロッピー)だと0xf0を指定します。
11行目は一個のFAT(ファイルやディレクトリについての情報を記録する特殊なシステム領域)あたりのセクタ数を指定しています。
12行目はトラックという単位が何セクタで構成されているかを指定しています。普通のフロッピーでは18セクタです。
13行目は磁気ヘッドの数を指定します。
表と裏があるので2です。 14行目はこのボリュームの手前に存在するセクタ数で、パーティションを使っていないので0を指定しています。
15行目はドライブの総セクタ数が0x10000を超えるときにドライブの総セクタ数を指定します。 今回は超えていないので0を指定しています。
16行目はBIOSで使われるドライブ番号でフロッピーだと0x00を指定します。
17行目はWINDOWSで使う領域で当然WINDOWSを使わないので0です。
18行目は以下三行の設定が存在している場合0x29を指定します。
19行目はボリュームを識別するための番号で8桁の十六進数で指定します。
20行目はディスクの名前を11byteの文字列で指定します。
21行目はフォーマットのタイプを8byteの文字列で指定します。今回はFAT12です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
    JMP     entry
    DB      0x90
    DB      "HELLOAPC"      ; ブートセクタの名前
    DW      512             ; 1セクタの大きさ
    DB      1               ; クラスタの大きさ
    DW      1               ; FATがどこから始まるか
    DB      2               ; FATの個数
    DW      224             ; ルートディレクトリ領域の大きさ
    DW      2880            ; このドライブの大きさ
    DB      0xf0            ; メディアのタイプ
    DW      9               ; FAT領域の長さ
    DW      18              ; 1トラックにいくつのセクタがあるか
    DW      2               ; ヘッドの数
    DD      0               ; パーティションを使ってないため0
    DD      0               ; 総セクタ数<0x10000より0
    DB      0x00            ; フロッピーディスクでは0x00
    DB      0               ; WindowsNT予約領域
    DB      0x29            ; 下の3つの設定が存在することを示す。
    DD      0xffffffff      ; ボリュームシリアル番号
    DB      "APC        "   ; ディスクの名前(11バイト)
    DB      "FAT12   "      ; フォーマットの名前(8バイト)

何行目に何をしているかはわかったけどDBやらDDやらDWは何だと思われたかと思います。DBはファイルに1byte書き込むという命令です。DWは2byte、DDは4byteです。 それじゃあDB "HELLOAPC"などのDBのあとに文字列が来るものは何だ、明らかに1byteではないじゃないかと感じると思いますがDB "文字列"とすると文字列を構成するそれぞれの文字の文字コードをしらべて1byteずつ書き込んでくれるようになっています。 また0xで始まる数字はそれが16進数であることを示しています。0x12は10進数で18です。

ここから先がプログラムとなっていてここではレジスタに0を代入して初期化しています。 レジスタとはCPUの中にある高速な記憶装置のことです。 MOV A,BはAにBを代入するという命令です。

1
2
3
4
5
entry:
		MOV		AX,0
		MOV		DS,AX
		MOV		ES,AX
		MOV		SS,AX

次に1行目であとに書いてある表示するメッセージの最初のメモリの番地をSIレジスタに代入しています。 msgがフロッピーの中のどこの番地に当たるのかを計算してその番地がSIに代入されます。 そこから先はAXとBSレジスタにテキストVRAMの領域の最初の番地を代入して、後々使うのでBXレジスタに0x0000を代入しています。

1
2
3
4
5
print:
		MOV		SI,msg
		MOV		AX,0xa000
		MOV		ES,AX
		MOV		BX,0x0000

ここではまず1行目でALレジスタに次に書き込む文字のメモリ番地の中身(=文字コード)を代入しています。[]で囲むとメモリの番地を意味します。その中にCS:SIとありますが、これはCS*16+SIのメモリ番地を指定するという意味です。CS*16がこのプログラムが格納されているメモリーの番地を指すのでそこに文字データのFD内での番地を足すことで次に書き込まれる文字の番地を指定しています。
2,3行目はもしすべての文字をテキストVRAMに書き終わっているのならfin:に飛ぶという命令です。
2行目でALと0を比較するという命令をして、3行目でもしAL=0であったらfin:に飛ぶという命令をしています。 なぜこれで書き終わっていることを検知できるかといえば、後述する文字データが書かれている部分を見るとすべての文字列のあとに0x0を書き込んであります。一文字ずつ書き込んでいった後0x0が現れたことを確認して文字の書き込みが終わったことを検知しています。
4行目で実際にテキストVRAMに文字を書き込んでいます。[ES:BX]で書き込むべきメモリ番地を指定して(ESにはテキストVRAMの最初の番地が入っていてBXにはテキストVRAMの中で何バイト目に書き込む必要があるかが入っています)、さきほどALに文字コードを代入したのでそれを書き込んでいます。
5行目でSIに1を加えることで次の文字のFD内での番地がSIに入ります
6行目ではテキストVRAMの中での番地を1文字あたり2byte使うため2進めています。
7行目でputloop:に戻ってループしています。これによってまた次の文字の書き込みが始まります。

1
2
3
4
5
6
7
8
putloop:
		MOV		AL,[CS:SI]
		CMP		AL,0
		JZ		fin
		MOV		[ES:BX],AL
		INC		SI
		ADD		BX,2
		JMP		putloop

前述した通り文字をテキストVRAMに書き終わったらfin:に飛んでくるのでCPUを待機状態にします。

1
2
3
fin:
		HLT
		JMP		fin

ここに実際に表示する文字列が書き込まれています。前述した通り文字列の後に書き込まれている0x0によってテキストVRAMへの書き込みが終わったことを検知しています。

1
2
3
4
msg:
		DB "THANK YOU FOR COMING TO APC"
		DB "  -Asano Physics Club 2022-"
		DB 0x0

最後に0x400までを0x00で埋める命令を実行しています。 \(はこの行が先頭から何バイト目であるかを教えてくれる変数です。 0x400からそれを引くことによって何バイトを0x00で埋めればいいかを調べています。

1
RESB	0x400-\)

実際に動かす

https://github.com/asanobuturi/PC98APCOSに実行方法が書いてあります。

参考文献

川合秀美.30日でできるOS自作入門.マイナビ出版,2006 https://qiita.com/TakedaHiromasa/items/371503c48ac33237a859 https://github.com/TakedaHiromasa/HelloWorld-PC98 http://elm-chan.org/docs/fat.html

おわりに

展示用に作ったものの解説をしてみました。正直説明がわかりにくかったり、意味をあいまいにしている部分もあったと思います。すみません。ここまで読んでくださりありがとうございました。

ライセンス

https://github.com/TakedaHiromasa/HelloWorld-PC98のソースコードを改造して作らせていただきました。以下にライセンス表記があります。

MIT License

Copyright (c) 2018

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

次へ部内で鯖を飼育する>
前へPC98という古いPCで曲をビープ音で演奏してみた>