Top

第4章 ユーザーランド

アプリケーションとしては、shellとuptime、whoareyouという3つです。現状、カーネルの動作確認程度のものでしかないです。

shellはその名の通りシェルで、CUIを提供します。shellの組み込みコマンドとしては、echoとメモリ/IOへの直接read/writeのコマンド(readb,readw,readl,ioreadb,writeも同様のコマンド名)、そしてbgというコマンドがあります。bgは今回のリリースで追加したコマンドで、引数で指定したコマンドをバックグラウンド実行します。(これまでは、実行したコマンドの終了を待つことができなかったので、常にバックグラウンド実行でした。)uptime・whoareyouもshellから起動します。これらのコマンド名をshell上で入力すると、shellはexecシステムコール(OS5ではexecをシステムコールとしています)を使用して実行します。

uptimeはマルチタスクの動作確認をするためのコマンドです。コンソール画面の右上で、16進数で起動時間をカウントし続けます。自ら終了することがないコマンドなので、バックグラウンド実行しないとプロンプトが帰ってこなくなります。

whoareyouは今回のリリースで新たに追加したコマンドです。argcとargvによりコマンドライン引数を受け取れることを確認するためのコマンドです。

また、今回のリリースでは、main()をエントリポイントとする変更や、静的ライブラリの仕組みも導入しており、よく見るCのソースコードのようにアプリケーションを書けるようになりました。

話は変わって、ユーザーランドのファイルシステムイメージは、makeの過程で、シェルスクリプトで作成します。ファイルシステムは、簡単に、ファイル名とバイナリのみを管理するだけのもので、シェルスクリプトでバイナリを並べて連結しています。ファイルシステムについても詳しくは上述のスライドをご覧ください。

これまでをまとめると、アプリケーションが実行されるまでの流れは以下のとおりです。

  1. ファイルシステムイメージをブートローダーがRAM上の決まったアドレスへロード
  2. カーネルは、初期化の過程でファイルシステムが配置されているRAM上の領域をチェック
  3. カーネルは、ファイルシステム上の1つ目のファイルを、カーネル起動後に実行する最初のアプリとして実行する(ここでshellが実行される)

4.1 共通部分

apps/Makefile

リスト4.1: apps/Makefile

 1: LIB_DIR = .lib
 2: BIN_DIR = .bin
 3: APP_LD = ../app.ld
 4: LIB_DIRS = libkernel libcommon libconsole libstring
 5: APP_DIRS = $(shell find . -maxdepth 1 -type d '!' -iname '.*' '!'   \
 6: -iname 'include' '!' -iname 'lib*')
 7: CFLAGS = -Wall -Wextra
 8: CFLAGS += -nostdinc -nostdlib -fno-builtin -c
 9: CFLAGS += -I../include
10: CFLAGS += -m32
11: CFLAGS += -DCOMPILE_APP
12: LDFLAGS = -L../$(LIB_DIR)
13: LIBS = -lstring -lconsole -lcommon -lkernel
14: 
15: apps.img: lib app
16:     ../tools/make_os5_fs.sh $(BIN_DIR)/* > $@
17: 
18: lib: $(LIB_DIRS)
19:     [ -d $(LIB_DIR) ] || mkdir $(LIB_DIR)
20:     for libdir in $^; do \
21:             make -C $$libdir LIB_DIR=../$(LIB_DIR)  \
22:             APP_LD=$(APP_LD) CFLAGS="$(CFLAGS)"   \
23:             LDFLAGS="$(LDFLAGS)" LIBS="$(LIBS)"; \
24:     done
25: 
26: app: $(APP_DIRS)
27:     [ -d $(BIN_DIR) ] || mkdir $(BIN_DIR)
28:     for appdir in $^; do \
29:             make -C $$appdir BIN_DIR=../$(BIN_DIR)  \
30:             APP_LD=$(APP_LD) CFLAGS="$(CFLAGS)"   \
31:             LDFLAGS="$(LDFLAGS)" LIBS="$(LIBS)"; \
32:     done
33: 
34: clean:
35:     rm -rf *~ *.o *.a *.bin *.dat *.img *.map $(LIB_DIR) $(BIN_DIR)
36:     for dir in $(LIB_DIRS) $(APP_DIRS); do \
37:             make -C $$dir clean; \
38:     done
39: 
40: .PHONY: lib app clean

ユーザーランド上の各アプリケーションのコンパイルを行う大元のMakefileです。

このMakefileでの最終生成物はapps.imgです。apps.imgを生成するために、ライブラリのコンパイル(libターゲット)、アプリケーションのコンパイル(appターゲット)、tools/make_os5_fs.shでファイルシステム生成(apps.imgターゲット)という流れです。

apps/app.ld

リスト4.2: apps/app.ld

 1: OUTPUT_FORMAT("binary");
 2: 
 3: SECTIONS
 4: {
 5:     . = 0x20000030;
 6:     .text   : {
 7:             *(.entry)
 8:             *(.text)
 9:     }
10:     .rodata : {
11:             *(.strings)
12:             *(.rodata)
13:             *(.rodata.*)
14:     }
15:     .data   : {*(.data)}
16:     .bss    : {*(.bss)}
17: }

アプリケーションのリンカスクリプトです。仮想アドレス空間上、ユーザー空間は0x2000 0000からで、先頭48(0x30)バイトがファイルシステムのヘッダなので、0x2000 0030からtextセクション(コードの領域)を配置しています。そのため、スケジューラでもタスクのエントリアドレスは0x2000 0030としています(kernel/task.cの#define APP_ENTRY_POINT 0x20000030)。

apps/include/app.h

リスト4.3: apps/include/app.h

 1: #ifndef _APP_H_
 2: #define _APP_H_
 3: 
 4: int main(int argc, char *argv[]) __attribute__((section(".entry")));
 5: 
 6: #endif /* _APP_H_ */

エントリ関数としてmain関数のプロトタイプ宣言をしています。

4.2 0shell

apps/0shell/Makefile

リスト4.4: apps/0shell/Makefile

 1: NAME=0shell
 2: 
 3: $(BIN_DIR)/$(NAME): $(NAME).o
 4:     ld -m elf_i386 -o $@ $< -Map $(NAME).map -s -T $(APP_LD) -x  \
 5:     $(LDFLAGS) $(LIBS)
 6: 
 7: %.o: %.c
 8:     gcc $(CFLAGS) -o $@ $<
 9: 
10: clean:
11:     rm -rf *~ *.o *.map
12: 
13: .PHONY: clean

シェルアプリケーション(0shell)のMakefileです。Makefileはアプリケーションごとに存在しますが、内容はNAME=0shellの行を除いて共通です。

apps/0shell/0shell.c

リスト4.5: apps/0shell/0shell.c

 1: #include <app.h>
 2: #include <kernel.h>
 3: #include <common.h>
 4: #include <string.h>
 5: #include <console.h>
 6: 
 7: #define MAX_LINE_SIZE       512
 8: 
 9: enum {
10:     ECHO,
11:     READB,
12:     READW,
13:     READL,
14:     IOREADB,
15:     WRITEB,
16:     WRITEW,
17:     WRITEL,
18:     IOWRITEB,
19:     BG,
20: #ifdef DEBUG
21:     TEST,
22: #endif /* DEBUG */
23:     COMMAND_NUM
24: } _COMMAND_SET;
25: 
26: static int command_echo(char *args)
27: {
28:     put_str(args);
29:     put_str("\r\n");
30: 
31:     return 0;
32: }
33: 
34: static int command_readb(char *args)
35: {
36:     char first[128], other[128];
37:     unsigned char *addr;
38: 
39:     str_get_first_entry(args, first, other);
40:     addr = (unsigned char *)str_conv_ahex_int(first);
41:     dump_hex(*addr, 2);
42:     put_str("\r\n");
43: 
44:     return 0;
45: }
46: 
47: static int command_readw(char *args)
48: {
49:     char first[128], other[128];
50:     unsigned short *addr;
51: 
52:     str_get_first_entry(args, first, other);
53:     addr = (unsigned short *)str_conv_ahex_int(first);
54:     dump_hex(*addr, 4);
55:     put_str("\r\n");
56: 
57:     return 0;
58: }
59: 
60: static int command_readl(char *args)
61: {
62:     char first[128], other[128];
63:     unsigned int *addr;
64: 
65:     str_get_first_entry(args, first, other);
66:     addr = (unsigned int *)str_conv_ahex_int(first);
67:     dump_hex(*addr, 8);
68:     put_str("\r\n");
69: 
70:     return 0;
71: }
72: 
73: static int command_ioreadb(char *args)
74: {
75:     char first[128], other[128];
76:     unsigned short addr;
77: 
78:     str_get_first_entry(args, first, other);
79:     addr = (unsigned short)str_conv_ahex_int(first);
80:     dump_hex(inb_p(addr), 2);
81:     put_str("\r\n");
82: 
83:     return 0;
84: }
85: 
86: static int command_writeb(char *args)
87: {
88:     char first[16], second[32], other[128], _other[128];
89:     unsigned char data, *addr;
90: 
91:     str_get_first_entry(args, first, other);
92:     str_get_first_entry(other, second, _other);
93:     data = (unsigned char)str_conv_ahex_int(first);
94:     addr = (unsigned char *)str_conv_ahex_int(second);
95:     *addr = data;
96: 
97:     return 0;
98: }
99: 
100: static int command_writew(char *args)
101: {
102:    char first[16], second[32], other[128], _other[128];
103:    unsigned short data, *addr;
104: 
105:    str_get_first_entry(args, first, other);
106:    str_get_first_entry(other, second, _other);
107:    data = (unsigned short)str_conv_ahex_int(first);
108:    addr = (unsigned short *)str_conv_ahex_int(second);
109:    *addr = data;
110: 
111:    return 0;
112: }
113: 
114: static int command_writel(char *args)
115: {
116:    char first[16], second[32], other[128], _other[128];
117:    unsigned int data, *addr;
118: 
119:    str_get_first_entry(args, first, other);
120:    str_get_first_entry(other, second, _other);
121:    data = (unsigned int)str_conv_ahex_int(first);
122:    addr = (unsigned int *)str_conv_ahex_int(second);
123:    *addr = data;
124: 
125:    return 0;
126: }
127: 
128: static int command_iowriteb(char *args)
129: {
130:    char first[16], second[32], other[128], _other[128];
131:    unsigned char data;
132:    unsigned short addr;
133: 
134:    str_get_first_entry(args, first, other);
135:    str_get_first_entry(other, second, _other);
136:    data = (unsigned char)str_conv_ahex_int(first);
137:    addr = (unsigned short)str_conv_ahex_int(second);
138:    outb_p(data, addr);
139: 
140:    return 0;
141: }
142: 
143: #ifdef DEBUG
144: static int command_test(char *args)
145: {
146:    put_str("test\r\n");
147: 
148:    return 0;
149: }
150: #endif /* DEBUG */
151: 
152: static unsigned char get_command_id(const char *command)
153: {
154:    if (!str_compare(command, "echo")) {
155:            return ECHO;
156:    }
157: 
158:    if (!str_compare(command, "readb")) {
159:            return READB;
160:    }
161: 
162:    if (!str_compare(command, "readw")) {
163:            return READW;
164:    }
165: 
166:    if (!str_compare(command, "readl")) {
167:            return READL;
168:    }
169: 
170:    if (!str_compare(command, "ioreadb")) {
171:            return IOREADB;
172:    }
173: 
174:    if (!str_compare(command, "writeb")) {
175:            return WRITEB;
176:    }
177: 
178:    if (!str_compare(command, "writew")) {
179:            return WRITEW;
180:    }
181: 
182:    if (!str_compare(command, "writel")) {
183:            return WRITEL;
184:    }
185: 
186:    if (!str_compare(command, "iowriteb")) {
187:            return IOWRITEB;
188:    }
189: 
190:    if (!str_compare(command, "bg")) {
191:            return BG;
192:    }
193: 
194: #ifdef DEBUG
195:    if (!str_compare(command, "test")) {
196:            return TEST;
197:    }
198: #endif /* DEBUG */
199: 
200:    return COMMAND_NUM;
201: }
202: 
203: int main(int argc __attribute__ ((unused)),
204:     char *argv[] __attribute__ ((unused)))
205: {
206:    while (1) {
207:            char buf[MAX_LINE_SIZE];
208:            char command[256], args[256];
209:            unsigned char command_id, is_background = 0;
210:            unsigned int fp;
211: 
212:            put_str("OS5> ");
213:            if (get_line(buf, MAX_LINE_SIZE) <= 0) {
214:                    continue;
215:            }
216: 
217:            while (1) {
218:                    str_get_first_entry(buf, command, args);
219:                    command_id = get_command_id(command);
220:                    if (command_id != BG)
221:                            break;
222:                    else {
223:                            is_background = 1;
224:                            copy_mem(args, buf,
225:                                     (unsigned int)str_get_len(args));
226:                    }
227:            }
228: 
229:            switch (command_id) {
230:            case ECHO:
231:                    command_echo(args);
232:                    break;
233:            case READB:
234:                    command_readb(args);
235:                    break;
236:            case READW:
237:                    command_readw(args);
238:                    break;
239:            case READL:
240:                    command_readl(args);
241:                    break;
242:            case IOREADB:
243:                    command_ioreadb(args);
244:                    break;
245:            case WRITEB:
246:                    command_writeb(args);
247:                    break;
248:            case WRITEW:
249:                    command_writew(args);
250:                    break;
251:            case WRITEL:
252:                    command_writel(args);
253:                    break;
254:            case IOWRITEB:
255:                    command_iowriteb(args);
256:                    break;
257: #ifdef DEBUG
258:            case TEST:
259:                    command_test(args);
260:                    break;
261: #endif /* DEBUG */
262:            default:
263:                    fp = syscall(SYSCALL_OPEN, (unsigned int)command, 0, 0);
264:                    if (fp) {
265:                            unsigned int argc = 0, i;
266:                            char *argv[256];
267:                            char *start;
268: 
269:                            argv[argc++] = command;
270: 
271:                            start = &args[0];
272:                            for (i = 0; ; i++) {
273:                                    if ((i == 0) && (args[i] == '\0')) {
274:                                            break;
275:                                    } else if ((args[i] == ' ') ||
276:                                               (args[i] == '\0')) {
277:                                            argv[argc++] = start;
278:                                            start = &args[i + 1];
279:                                            if (args[i] == ' ')
280:                                                    args[i] = '\0';
281:                                            else
282:                                                    break;
283:                                    }
284:                            }
285: 
286:                            syscall(SYSCALL_EXEC, fp, argc,
287:                                    (unsigned int)argv);
288: 
289:                            if (!is_background)
290:                                    syscall(SYSCALL_SCHED_WAKEUP_EVENT,
291:                                            EVENT_TYPE_EXIT, 0, 0);
292:                    } else
293:                            put_str("Command not found.\r\n");
294:                    break;
295:            }
296:    }
297: }

シェルアプリケーションの本体のソースコードです。OS5の中で3番目に長いソースコードで297行あります。

includeしている各ヘッダファイルについては以下の通りです。

app.h
main関数を使用するため
kernel.h
syscall関数を使用するため実体はapps/libkernel/libkernel.cにある
common.h
copy_mem関数を使用するため実体はapps/libcommon/libcommon.cにある
string.h
文字列操作関数を使用するため実体はapps/libstring/libstring.cにある
console.h
コンソール操作関数を使用するため実体はapps/libconsole/libconsole.cにある

main関数の処理をざっくりと説明すると以下の通りです。

  1. プロンプトを表示し、get_line関数で1行分の入力文字列を取得するまで待機(212~215行目)
  2. 1.の文字列からstr_get_first_entry関数でコマンド名を切り出し、get_command_id関数でシェル組み込みコマンドのIDを取得。取得したコマンドが"bg"コマンドであった場合は、is_backgroundフラグをセットする(217~227行目)
  3. コマンドIDに応じた処理を呼び出す(229~295行目)。組み込みコマンドではなかった場合、実行ファイルとみなし、openとexecを行う(263~294行目)

なお、3.でファイルを実行するときに、2.でis_backgroundがセットされていなければ、ファイルの実行が終了するまでシェルをスリープさせます(289~291行目)。これにより、バックグラウンド実行していない場合は、実行が終了するまでプロンプトが戻ってこないようにしています。

4.3 uptime

apps/uptime/Makefile

リスト4.6: apps/uptime/Makefile

 1: NAME=uptime
 2: 
 3: $(BIN_DIR)/$(NAME): $(NAME).o
 4:     ld -m elf_i386 -o $@ $< -Map $(NAME).map -s -T $(APP_LD) -x  \
 5:     $(LDFLAGS) $(LIBS)
 6: 
 7: %.o: %.c
 8:     gcc $(CFLAGS) -o $@ $<
 9: 
10: clean:
11:     rm -rf *~ *.o *.map
12: 
13: .PHONY: clean

uptimeアプリケーションのMakefileです。

apps/uptime/uptime.c

リスト4.7: apps/uptime/uptime.c

 1: #include <app.h>
 2: #include <kernel.h>
 3: #include <console.h>
 4: 
 5: int main(int argc __attribute__ ((unused)),
 6:      char *argv[] __attribute__ ((unused)))
 7: {
 8:     static unsigned int uptime;
 9:     unsigned int cursor_pos_y;
10: 
11:     while (1) {
12:             uptime = get_global_counter() / 1000;
13:             cursor_pos_y = get_cursor_pos_y();
14:             if (cursor_pos_y < ROWS) {
15:                     put_str_pos("uptime:", COLUMNS - (7 + 4), 0);
16:                     dump_hex_pos(uptime, 4, COLUMNS - 4, 0);
17:             } else {
18:                     put_str_pos("uptime:", COLUMNS - (7 + 4),
19:                                 cursor_pos_y - ROWS + 1);
20:                     dump_hex_pos(uptime, 4, COLUMNS - 4,
21:                                  cursor_pos_y - ROWS + 1);
22:             }
23:             syscall(SYSCALL_SCHED_WAKEUP_MSEC, 33, 0, 0);
24:     }
25: }

uptimeアプリケーションの本体のソースコードです。

uptimeは、OS5が起動してからの秒数を画面右上に表示(16進表記)するアプリケーションです。マルチタスクの動作確認のために作成しました。

処理としては、以下を無限ループで繰り返しています。

  1. 起動後の経過時間(グローバルカウンタ、ms単位)を取得(get_global_counter関数)し、秒へ変換(12行目)
  2. カーソルのY座標を取得(get_cursor_pos_y関数)(13行目)
  3. Y座標に応じた場所へ、1.の計算結果を16進数で出力(14~22行目)
  4. 33msスリープ(23行目)

3.は画面右上に固定させるための処理です。カーソルのY座標が1画面の行数(ROWS)未満であれば、まだ一度も画面スクロールを行っていないため、出力するY座標は0で良いです。カーソルのY座標がROWS以上の場合、画面はスクロールしているので、Y座標0の場所に出力しても見切れてしまいます(表示範囲外のため)。そのため、"カーソルY座標 - ROWS + 1"のY座標へ出力するようにしています(スクロール後、常にカーソルは画面最下行に張り付くため、この計算式になっています)。

4.は画面更新周期を作るためのスリープです。なお、33msは感覚的に決めた値です。スケジューリングの周期が「画面スクロール処理をしてから再度uptimeのカウンタが描画されるまでの間」に当たるとチラつきます。

4.4 whoareyou

apps/whoareyou/Makefile

リスト4.8: apps/whoareyou/Makefile

 1: NAME=whoareyou
 2: 
 3: $(BIN_DIR)/$(NAME): $(NAME).o
 4:     ld -m elf_i386 -o $@ $< -Map $(NAME).map -s -T $(APP_LD) -x  \
 5:     $(LDFLAGS) $(LIBS)
 6: 
 7: %.o: %.c
 8:     gcc $(CFLAGS) -o $@ $<
 9: 
10: clean:
11:     rm -rf *~ *.o *.map
12: 
13: .PHONY: clean

whoareyouアプリケーションのMakefileです。

apps/whoareyou/whoareyou.c

リスト4.9: apps/whoareyou/whoareyou.c

 1: #include <app.h>
 2: #include <kernel.h>
 3: #include <string.h>
 4: #include <console.h>
 5: 
 6: int main(int argc, char *argv[])
 7: {
 8:     if ((argc >= 2) && !str_compare(argv[1], "-v"))
 9:             put_str("Operating System 5\r\n");
10:     else
11:             put_str("OS5\r\n");
12:     exit();
13: 
14:     return 0;
15: }

「お前は誰だ」と問いかけるアプリケーションです。「OS5」と返してくれます。UNIXで実行したユーザーのユーザー名を表示する"whoami"コマンドのオマージュ(パロディ?)です。

これはコマンドライン引数の動作確認として用意したアプリケーションで、"-v"オプションをつけると「Operating System 5」と詳細に返してくれます。

4.5 libkernel

apps/libkernel/Makefile

リスト4.10: apps/libkernel/Makefile

 1: NAME=libkernel
 2: 
 3: $(LIB_DIR)/$(NAME).a: $(NAME).o
 4:     ar rcs $@ $<
 5: 
 6: %.o: %.c
 7:     gcc $(CFLAGS) -o $@ $<
 8: 
 9: clean:
10:     rm -rf *~ *.o *.a *.map
11: 
12: .PHONY: clean

カーネル回りのライブラリ"libkernel"のMakefileです。内容はNAME=libkernelの行を除いて他のライブラリのMakefileと同じです。

apps/include/kernel.h

リスト4.11: apps/include/kernel.h

 1: #ifndef _APP_KERNEL_H_
 2: #define _APP_KERNEL_H_
 3: 
 4: #include "../../kernel/include/kernel.h"
 5: #include "../../kernel/include/io_port.h"
 6: #include "../../kernel/include/console_io.h"
 7: 
 8: unsigned int syscall(unsigned int syscall_id, unsigned int arg1,
 9:                  unsigned int arg2, unsigned int arg3);
10: unsigned int get_global_counter(void);
11: void exit(void);
12: 
13: #endif /* _APP_KERNEL_H_ */

libkernelのヘッダファイルです。カーネルとアプリケーションの間のインタフェースはシステムコールのみです。libkernelではシステムコールを関数として提供します。

apps/libkernel/libkernel.c

リスト4.12: apps/libkernel/libkernel.c

 1: #include <kernel.h>
 2: 
 3: unsigned int syscall(unsigned int syscall_id, unsigned int arg1,
 4:                  unsigned int arg2, unsigned int arg3)
 5: {
 6:     unsigned int result;
 7: 
 8:     __asm__ (
 9:             "\tint $0x80\n"
10:     :"=a"(result)
11:     :"a"(syscall_id), "b"(arg1), "c"(arg2), "d"(arg3));
12: 
13:     return result;
14: }
15: 
16: unsigned int get_global_counter(void)
17: {
18:     return syscall(SYSCALL_TIMER_GET_GLOBAL_COUNTER, 0, 0, 0);
19: }
20: 
21: void exit(void)
22: {
23:     syscall(SYSCALL_EXIT, 0, 0, 0);
24: }

libkernelの本体です。システムコールを発行するsyscall関数を定義しています。また、get_global_counterexitは、syscallをラップしています。

4.6 libcommon

apps/libcommon/Makefile

リスト4.13: apps/libcommon/Makefile

 1: NAME=libcommon
 2: 
 3: $(LIB_DIR)/$(NAME).a: $(NAME).o
 4:     ar rcs $@ $<
 5: 
 6: %.o: %.c
 7:     gcc $(CFLAGS) -o $@ $<
 8: 
 9: clean:
10:     rm -rf *~ *.o *.a *.map
11: 
12: .PHONY: clean

共通で使用される関数をまとめるライブラリ"libcommon"のMakefileです。

apps/include/common.h

リスト4.14: apps/include/common.h

 1: #ifndef _COMMON_H_
 2: #define _COMMON_H_
 3: 
 4: int pow(int num, int multer);
 5: void copy_mem(const void *src, void *dst, unsigned int size);
 6: 
 7: #endif /* _COMMON_H_ */

libcommonのヘッダファイルです。べき乗計算を行うpow関数とデータのコピーを行うcopy_mem関数があります。

apps/libcommon/libcommon.c

リスト4.15: apps/libcommon/libcommon.c

 1: #include <common.h>
 2: 
 3: int pow(int num, int multer)
 4: {
 5:     if (multer == 0) return 1;
 6:     return pow(num, multer - 1) * num;
 7: }
 8: 
 9: void copy_mem(const void *src, void *dst, unsigned int size)
10: {
11:     unsigned char *d = (unsigned char *)dst;
12:     unsigned char *s = (unsigned char *)src;
13: 
14:     for (; size > 0; size--) {
15:             *d = *s;
16:             d++;
17:             s++;
18:     }
19: }

libcommonの本体です。

4.7 libconsole

apps/libconsole/Makefile

リスト4.16: apps/libconsole/Makefile

 1: NAME=libconsole
 2: 
 3: $(LIB_DIR)/$(NAME).a: $(NAME).o
 4:     ar rcs $@ $<
 5: 
 6: %.o: %.c
 7:     gcc $(CFLAGS) -o $@ $<
 8: 
 9: clean:
10:     rm -rf *~ *.o *.a *.map
11: 
12: .PHONY: clean

コンソールに関するライブラリlibconsoleのMakefileです。

apps/include/console.h

リスト4.17: apps/include/console.h

 1: #ifndef _CONSOLE_H_
 2: #define _CONSOLE_H_
 3: 
 4: unsigned int get_cursor_pos_y(void);
 5: void put_str(char *str);
 6: void put_str_pos(char *str, unsigned char x, unsigned char y);
 7: void dump_hex(unsigned int val, unsigned int num_digits);
 8: void dump_hex_pos(unsigned int val, unsigned int num_digits,
 9:               unsigned char x, unsigned char y);
10: unsigned int get_line(char *buf, unsigned int buf_size);
11: 
12: #endif /* _CONSOLE_H_ */

libconsoleのヘッダファイルです。以下の関数を提供しています。

apps/libconsole/libconsole.c

リスト4.18: apps/libconsole/libconsole.c

 1: #include <console.h>
 2: #include <kernel.h>
 3: 
 4: unsigned int get_cursor_pos_y(void)
 5: {
 6:     return syscall(SYSCALL_CON_GET_CURSOR_POS_Y, 0, 0, 0);
 7: }
 8: 
 9: void put_str(char *str)
10: {
11:     syscall(SYSCALL_CON_PUT_STR, (unsigned int)str, 0, 0);
12: }
13: 
14: void put_str_pos(char *str, unsigned char x, unsigned char y)
15: {
16:     syscall(SYSCALL_CON_PUT_STR_POS, (unsigned int)str,
17:             (unsigned int)x, (unsigned int)y);
18: }
19: 
20: void dump_hex(unsigned int val, unsigned int num_digits)
21: {
22:     syscall(SYSCALL_CON_DUMP_HEX, val, num_digits, 0);
23: }
24: 
25: void dump_hex_pos(unsigned int val, unsigned int num_digits,
26:               unsigned char x, unsigned char y)
27: {
28:     syscall(SYSCALL_CON_DUMP_HEX_POS, val, num_digits,
29:             (unsigned int)((x << 16) | y));
30: }
31: 
32: unsigned int get_line(char *buf, unsigned int buf_size)
33: {
34:     return syscall(SYSCALL_CON_GET_LINE, (unsigned int)buf, buf_size, 0);
35: }

libconsoleの本体です。いずれの関数もシステムコールをラップしたものです。

4.8 libstring

apps/libstring/Makefile

リスト4.19: apps/libstring/Makefile

 1: NAME=libstring
 2: 
 3: $(LIB_DIR)/$(NAME).a: $(NAME).o
 4:     ar rcs $@ $<
 5: 
 6: %.o: %.c
 7:     gcc $(CFLAGS) -o $@ $<
 8: 
 9: clean:
10:     rm -rf *~ *.o *.a *.map
11: 
12: .PHONY: clean

文字列操作ライブラリlibstringのMakefileです。

apps/include/string.h

リスト4.20: apps/include/string.h

 1: #ifndef _STRING_H_
 2: #define _STRING_H_
 3: 
 4: int str_get_len(const char *src);
 5: int str_find_char(const char *src, char key);
 6: void str_get_first_entry(const char *line, char *first, char *other);
 7: int str_conv_ahex_int(const char *hex_str);
 8: int str_compare(const char *src, const char *dst);
 9: 
10: #endif /* _STRING_H_ */

libstringのヘッダファイルです。以下の関数を提供しています。

apps/libstring/libstring.c

リスト4.21: apps/libstring/libstring.c

 1: #include <string.h>
 2: #include <common.h>
 3: 
 4: int str_get_len(const char *src)
 5: {
 6:     int len;
 7:     for (len = 0; src[len] != '\0'; len++);
 8:     return len + 1;
 9: }
10: 
11: int str_find_char(const char *src, char key)
12: {
13:     int i;
14: 
15:     for (i = 0; src[i] != key; i++) {
16:             if (src[i] == '\0') {
17:                     i = -1;
18:                     break;
19:             }
20:     }
21: 
22:     return i;
23: }
24: 
25: void str_get_first_entry(const char *line, char *first, char *other)
26: {
27:     int line_len, first_len, other_len;
28: 
29:     line_len = str_get_len(line);
30:     first_len = str_find_char(line, ' ');
31:     if (first_len < 0) {
32:             copy_mem((void *)line, (void *)first, line_len);
33:             first_len = line_len;
34:             other_len = 0;
35:             other[other_len] = '\0';
36:     } else {
37:             copy_mem((void *)line, (void *)first, first_len);
38:             first[first_len] = '\0';
39:             first_len++;
40:             other_len = line_len - first_len;
41:             copy_mem((void *)(line + first_len), (void *)other, other_len);
42:     }
43: 
44: #ifdef DEBUG
45:     shell_put_str(line);
46:     shell_put_str("|");
47:     shell_put_str(first);
48:     shell_put_str(":");
49:     shell_put_str(other);
50:     shell_put_str("\r\n");
51: #endif /* DEBUG */
52: }
53: 
54: int str_conv_ahex_int(const char *hex_str)
55: {
56:     int len = str_get_len(hex_str);
57:     int val = 0, i;
58: 
59:     for (i = 0; hex_str[i] != '\0'; i++) {
60:             if (('0' <= hex_str[i]) && (hex_str[i] <= '9')) {
61:                     val += (hex_str[i] - '0') * pow(16, len - 2 - i);
62:             } else {
63:                     val += (hex_str[i] - 'a' + 10) * pow(16, len - 2 - i);
64:             }
65:     }
66: 
67:     return val;
68: }
69: 
70: int str_compare(const char *src, const char *dst)
71: {
72:     char is_equal = 1;
73: 
74:     for (; (*src != '\0') && (*dst != '\0'); src++, dst++) {
75:             if (*src != *dst) {
76:                     is_equal = 0;
77:                     break;
78:             }
79:     }
80: 
81:     if (is_equal) {
82:             if (*src != '\0') {
83:                     return 1;
84:             } else if (*dst != '\0') {
85:                     return -1;
86:             } else {
87:                     return 0;
88:             }
89:     } else {
90:             return (int)(*src - *dst);
91:     }
92: }

libstringの本体です。特に変わったことはしていないです。


Top