みけCATのにっき(仮)
つれづれなるまゝに、日くらし、PCにむかひて、心に移りゆくよしなし事を、そこはかとなく書きつくれば、あやしうこそものぐるほしけれ。
(本当か!?)
出典

C言語のchar型の変数をVRAMに割り当てて文字を表示

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

C言語のchar型の変数をVRAMに割り当てて文字を表示

投稿記事 by みけCAT » 9年前

年末からC言語を始めました • C言語交流フォーラム ~ mixC++ ~
において、
みけCAT さんが書きました:リンカスクリプト(?)やnasmで出力に使用するメモリマップドな出力ポートやVRAMの要素をchar型の「変数」に割り当て、そこに代入することで出力や表示をすることはできる…?
という発言をしたので、実際にできるかやってみました。

自作の「OSっぽい何か」のMBR、BPB、 protected_load_with_api.asmを用いて、
C言語のコンパイル結果をロードし、実行します。
今回のコンパイル結果はたまたま__main関数のプログラムが.textセクションの最初に来ており、ヒープ領域も使用されなかったので、
.textセクションの中身を適当なファイルにコピペしてPROGRAM.BINとしてディスクのルートに置くことで読み込み、うまく実行することができました。

まずは、char型の変数を用いて1文字表示してみます。
VRAMに文字とアトリビュート(文字の色と背景の色)を書き込みます。

hoge.asm

CODE:

absolute 0xb8000
global c1
global _c1
global a1
global _a1
c1:
_c1:
	resb 1
a1:
_a1:
	resb 1
fuga.c

CODE:

extern volatile char c1, a1;

void __main(void) {
	c1 = 'A';
	a1 = 0x0f;
	for(;;);
}
Makefile

CODE:

fuga_exe.txt: fuga.exe
	objdump -d fuga.exe > fuga_ext.txt

fuga.exe: fuga.o hoge.o
	gcc -nostdlib -static -o fuga.exe fuga.o hoge.o

fuga.o: fuga.c
	gcc -c -O2 -o fuga.o fuga.c

hoge.o: hoge.asm
	nasm -f elf32 -o hoge.o hoge.asm
このコードを実行すると、文字「A」が画面の左上に表示されました。
nasm_display_char-20160110.png
char型の変数を用いた文字の表示
nasm_display_char-20160110.png (26.73 KiB) 閲覧数: 1086 回
さらに、char型の配列を用いて文字列を表示してみます。
せっかくなのでカラフルにしてみました。
また、カーソルを動かす関数をnasm側に書いてみました。

hoge.asm

CODE:

bits 32
section .text
global move_cursor, _move_cursor
	; void move_cursor(int x, int y)
move_cursor:
_move_cursor:
	push ebp
	mov ebp, esp
	push ebx
	; determine I/O address
	mov dx, 0x03cc
	in al, dx
	xor cx, cx
	test al, 1
	jz not_d
	mov cx, 0x0020
not_d:
	; calculate address
	mov eax, [ebp + 12]
	mov ebx, 80
	mul ebx
	add eax, [ebp + 8]
	mov ebx, eax
	; write cursor location
	pushf
	cli
	mov dx, 0x03b4 ; index of Cursor Location High
	add dx, cx
	mov al, 0x0e
	out dx, al
	mov dx, 0x03b5 ; data of Cursor Location High
	add dx, cx
	mov al, bh
	out dx, al
	mov dx, 0x03b4 ; index of Cursor Location Low
	add dx, cx
	mov al, 0x0f
	out dx, al
	mov dx, 0x03b5 ; data of Cursor Location Low
	add dx, cx
	mov al, bl
	out dx, al
	popf
	pop ebx
	leave
	ret

absolute 0xb8000
global vram, _vram
vram:
_vram:
	resb 2 * 80 * 25
fuga.c

CODE:

extern volatile char vram[];
extern void move_cursor(int x, int y);

void __main(void) {
	char hello[] = "hello, world!";
	int i;
	for(i = 0; hello[i] != '\0'; i++) {
		vram[i * 2] = hello[i];
		vram[i * 2 + 1] = (i % 6) + 9;
	}
	move_cursor(i, 0);
	for(;;);
}
Makefileは文字の表示と同じです。

実行結果
nasm_display_string-20160110.png
char型の配列変数を用いた文字列の表示
nasm_display_string-20160110.png (28.67 KiB) 閲覧数: 1105 回
オフトピック
今回はたまたまうまく動きましたが、このC言語のコードは識別子がアンダースコア2個から始まる関数を定義しているので、undefined behaviorになりそうです。

しかし、
Using and Porting the GNU Compiler Collection (GCC) - GCCコマンド・オプション
にあるように-lgccを指定し、void __main(void)をint main(void)に変えてビルドしたところatexitが無いというエラーが出たので、

CODE:

void atexit(void){}
という定義を追加したところ、
このatexit関数が.textセクションの先頭(0x401000)に来てしまい、しかもヘッダに書かれているエントリポイントも0x401000が指定されているため、うまく動かなそうでした。

nasmで__main関数にあたるものを定義し、main関数を呼び出すようにするといいかもしれない…と思いましたが、
同様に__mainが先頭に来ないのにエントリポイント情報が先頭を指してしまい、ダメでした。

どうすればいいのでしょうか…と言いたいところですが、ここで質問をしてしまうとStack Overflowで質問をするときにマルチポストになってしまい、
めんどくさいかもしれないので、このofftopicの内容は質問ではありません。

【追記】
識別子の先頭にアンダースコアが付くか付かないかを決め打ちにした場合、
エントリポイントをmain以外の名前にした上で(こうすると__mainへの依存が切れる)、
-eオプションを使用してエントリポイントのシンボルを指定するとよさそうです。
最後に編集したユーザー みけCAT on 2016年1月10日(日) 13:49 [ 編集 1 回目 ]
理由: 解決法を追記

コメントはまだありません。