OSが書けなくても画面にお絵描きがしたい

PCでお絵描きがしたい、けど…

冬です

外は寒いのでこたつに入ってPCでお絵描きでもしたいところです

しかし、筆者のパソコンはOSもペイントソフトも入っておらずお絵描きをすることができません

どうしてもお絵描きがしたいので、今日は頑張ってPCの画面にお絵描きできるようにします

この記事はWORDIAN Advent Calendar 2018 - 23日目の記事です

戦略

さて、PCを動かすためにまずはブートローダが必要です。ブートローダBIOSの仕様に沿って実装する訳ですが、アセンブリを書いてプログラムがメモリの0x7c00にロードされ...32bitプロテクトモードに移行して...のように非常に難解なことをしないといけません。冬が終わってしまいます。世の中は便利なものでGRUBと呼ばれる素晴らしいブートローダが存在するため、ありがたく利用することにします。GRUBはよろしくやってくれた後にフレームバッファの場所を教えてくれるのでそこに色を書き込むことでお絵描きができます。

あ、GRUBを用意したりプログラムをコンパイルしたりするためのPCは別途用意することにしました。

GRUBを用意する

PCにGRUBをインストールします。ArchLinuxの人は

sudo pacman -S grub

で入ります。

GRUBで読み込めるプログラムの形式について

GRUBではmultibootと呼ばれるプログラムの形式を使用することができます。お絵描きするために最低限必要な事項は本記事で記述しますが、詳しい仕様についてはMultiboot Specification version 0.6.96をご確認ください。

Multiboot Specificationの3.1 OS image formatではプログラムの形式についての規定がされています。いろいろ書いてありますが、どうやらプログラムにはMultiboot headerが必要でファイルの先頭8192バイトに格納されている必要があるようです。3.2 Machine stateと3.3 Boot information formatではmultibootが読み込まれた後にGRUBが返してくれるマシンの状態に関する記述があります。これについても後述します。

Multibot headerの形式

Offset Type Field Name Note
0 unsigned 32bit magic required
4 unsigned 32bit flags required
8 unsigned 32bit checksum required
12 unsigned 32bit header_addr if flags[16] is set 今回は使わない
16 unsigned 32bit load_addr if flags[16] is set 今回は使わない
20 unsigned 32bit load_end_addr if flags[16] is set 今回は使わない
24 unsigned 32bit bss_end_addr if flags[16] is set 今回は使わない
28 unsigned 32bit entry_addr if flags[16] is set 今回は使わない
32 unsigned 32bit mode_type if flags[2] is set
36 unsigned 32bit width if flags[2] is set
40 unsigned 32bit height if flags[2] is set
44 unsigned 32bit depth if flags[2] is set
  • magic
    マジックナンバーです。必ず0x1BADB002を格納します。

  • flags
    bit 0,1,2,15を使います。bit 2を1にするとビデオモードを設定することができます。今回はとりあえず1にしましょう。 bit 0,1,15はそれぞれ1,1,0にしておきます。お絵描きに関係ないので説明は割愛します。

  • checksum
    チェックサムです。-(magic+flags)を格納します。

  • mode_type
    ビデオモードの希望です。グラフィックモード(0)かテキストモード(1)のどちらかに設定することができます。お絵描きするにはグラフィックモードのほうが嬉しいので今回は0に設定しておきます。

  • width,height
    画面のサイズの希望を設定することができます。お絵描きにおけるキャンパスサイズとなります。とりあえず最初はwidthを640、heightを480くらいにしておくことにします。

  • depth
    ビット深度の希望です。32にします。

  • その他
    今回は使わないため説明は割愛します。値は適当に0にしておきましょう。

multibootが読み込まれた後のマシンのレジスタの状態

色々と記述がありますがとりあえずEBXとついて気にしておけば良さそうです。

GRUBから渡されるブート情報について

multibootがGRUBに読み込まれるとEBXにブート情報構造体のポインタが格納されます。お絵描きをするために必要なフレームバッファの情報もあります。

             +-------------------+
     0       | flags             |    (required)
             +-------------------+
     4       | mem_lower         |    (present if flags[0] is set)
     8       | mem_upper         |    (present if flags[0] is set)
             +-------------------+
     12      | boot_device       |    (present if flags[1] is set)
             +-------------------+
     16      | cmdline           |    (present if flags[2] is set)
             +-------------------+
     20      | mods_count        |    (present if flags[3] is set)
     24      | mods_addr         |    (present if flags[3] is set)
             +-------------------+
     28 - 40 | syms              |    (present if flags[4] or
             |                   |                flags[5] is set)
             +-------------------+
     44      | mmap_length       |    (present if flags[6] is set)
     48      | mmap_addr         |    (present if flags[6] is set)
             +-------------------+
     52      | drives_length     |    (present if flags[7] is set)
     56      | drives_addr       |    (present if flags[7] is set)
             +-------------------+
     60      | config_table      |    (present if flags[8] is set)
             +-------------------+
     64      | boot_loader_name  |    (present if flags[9] is set)
             +-------------------+
     68      | apm_table         |    (present if flags[10] is set)
             +-------------------+
     72      | vbe_control_info  |    (present if flags[11] is set)
     76      | vbe_mode_info     |
     80      | vbe_mode          |
     82      | vbe_interface_seg |
     84      | vbe_interface_off |
     86      | vbe_interface_len |
             +-------------------+
     88      | framebuffer_addr  |    (present if flags[12] is set)
     96      | framebuffer_pitch |
     100     | framebuffer_width |
     104     | framebuffer_height|
     108     | framebuffer_bpp   |
     109     | framebuffer_type  |
     110-115 | color_info        |
             +-------------------+

色々ありますが必要な部分はオフセット88からのフレームバッファに関する情報のみです。

Offset Type Field Name
88 unsigned 64bit framebuffer_addr
96 unsigned 32bit framebuffer_pitch
100 unsigned 32bit framebuffer_width
104 unsigned 32bit framebuffer_height
108 unsigned 32bit framebuffer_bpp
109 unsigned 32bit framebuffer_type
110-115 unsigned 32bit color_info
  • framebuffer_addr
    フレームバッファの先頭のアドレスです。64bitです。注意

  • framebuffer_pitch
    バイトのピッチです。単位はバイト

  • framebuffer_width , framebuffer_hight GRUBが設定したフレームバッファの縦横のサイズです。

  • framebuffer_bpp GRUBが設定したフレームバッファのビット深度です。

  • framebuffer_type GRUBが設定したフレームバッファの種類です。0だとインデックスカラー、1だとダイレクトカラー、2だとテキストモードになっています。また、0,1ではこの後のcolor_infoが使用されます。

  • color_info
    framebuffer_typeが1(ダイレクトカラーの場合)

             +----------------------------------+
     110     | framebuffer_red_field_position   |
     111     | framebuffer_red_mask_size        |
     112     | framebuffer_green_field_position |
     113     | framebuffer_green_mask_size      |
     114     | framebuffer_blue_field_position  |
     115     | framebuffer_blue_mask_size       |
             +----------------------------------+

32bit中に各色がどのように配置されるかが格納されています。positionが下位ビットから何bit目からかを、mask_sizeがビットの幅を表しています。 今回はダイレクトカラーに設定されることを前提とするため、インデックスカラーの場合の説明は割愛します。

実装

Multiboot Specificationの4.3 Example OS codeにはサンプルコードがあります。せっかくなので利用します。

プリプロセッサ定義や構造体が含まているmultiboot.hはそのまま使います。

multiboot.h (クリックで展開)

boot.SではMultiboot headerを配置してC言語コンパイルした関数を呼びます。サンプルから不要な部分を削除して最小限必要な部分のみを残しました。

画像データを描画するCプログラムです。ダイレクトカラーでdepthが32bitに設定されていることが前提です。とりあえず画面を緑色で塗りました。

コードが書けたら

$ gcc -m32 -ffreestanding -fno-common -fno-builtin -fomit-frame-pointer -O2 -c -o oekaki.o oekaki.c
$ gcc -m32 -ffreestanding -fno-common -fno-builtin -fomit-frame-pointer -O2 -c -o boot.o boot.S
$ ld  -melf_i386  -Ttext=0x100000 --oformat elf32-i386 -o Oekaki boot.o oekaki.o

のようにコンパイルとリンクを行います。テキストエディタgrub.cfgも作成しましょう。

menuentry "oekaki" {
    multiboot /boot/Oekaki
    boot
}

grub-mkrescueでブートイメージを作成します。

$ mkdir -p iso/boot/grub
$ mv Oekaki iso/boot
$ mv grub.cfg iso/boot/grub
$ grub-mkrescue -o oekaki.iso iso

作成したプログラムのブートイメージが作成されました。試しにqemuでブートしてみましょう。 f:id:akkkix:20181224021228p:plain

これでフレームバッファの中身を画面に出せるようになりました。後はソースコードを適当にいじれば画像等も表示することができます。

めでたしめでたし

JRの指定席の空席を確認するSlack botを作った

この記事は#一人GWハッカソンの記事です

前置き(?)

JRにはサイバーステーションというWeb上で指定席の空き状況が確認できるサービスがあります。 今回はこのサイバーステーションから空席情報を取得するbotを作ってみました。

f:id:akkkix:20180505205251p:plain

内部ではseleniumとNokogiriで情報を取得しています。

副産物(というか今回のメイン)

人気列車の指定席は発売開始後すぐ売れ切れてしまい、なかなか取れないことがあります。
そういった列車の指定席を取るためにはキャンセルを待たなければなりません。しかし、いちいち空席情報を確認するのは非常に面倒くさい作業です。
この面倒臭さを解消するために空席情報を監視するプログラムを書きました。 f:id:akkkix:20180505210844p:plain このように取得したい列車の情報をSQLでDBに登録して実行すると... f:id:akkkix:20180505211806p:plain このように変化を通知してくれます。

あとはこのプログラムをcronで定期実行させSlack等に通知が行くようにすれば、空席が発生し通知を受信した瞬間にえきねっとやe5489で座席を購入することができるでしょう。

githubリポジトリ

github.com

MNPのためのLinksMate(検証)

MNP弾としてLinksMateを利用してみました。

申し込み→転出までの費用

項目 金額
1GBプラン¥500(日割り適用) ¥323
音声通話機能 ¥600
ユニバーサルサービス ¥2
新規契約事務手数料 ¥3000
SIMカード発行手数料 ¥400
MNP予約番号発行手数料 ¥2000
SIMカード削除事務手数料 ¥3000
消費税 ¥746
合計 ¥10071

手続きの流れ

(1日目)
↓申し込み開始
↓書類審査通過

(2日目)
SIMカード発送
MNP番号申請

(3日目)
SIMカード到着
MNP番号通知

MNP転出→解約

申し込みからMNP番号発行まで3日を要しました。

データ料金の日割りについて

解約月は日割りは適用されませんが契約月に解約する場合は日割り適用になります。データ容量も日割りになります。

料金発生が一日のみの場合(一月31日で計算しました)
(1/31)*500+他料金(9002)+税 = 9740

申し込み完了日の5日後が料金発生日です。

linksmate.jp

linksmate.jp

初月の解約について

一部記事でMNP転出は契約翌月から可能との記述があるが誤り。
契約月の転出は可能(サポートに確認済

まとめ

  • 申し込みからMNP番号発行まで早ければ3日

  • うまくやれば¥10000以内での転出が可能

  • 契約月に解約した場合は日割の適用になる

  • 初月MNP転出可

その他

docomoMVNOでもdocomoMNP転入は可能です(DS店員に確認済み

事務手数料¥1000割引キャンペーン期間なら8000円台での転出が可能?

秋の大感謝祭キャンペーン | LinksMate(リンクスメイト) - ゲームプレイヤーのためのお得なSIM

(TC安すぎ...)

セキュリティ・キャンプ2016の思い出

ポンコツ過ぎるし、頭大丈夫じゃない気がする…

応募まで

気づいたら締め切り3日前だった。
土日寝ないで書いたから文章がヤバい。
応募するだけでもためになると思う。

選考結果発表

授業中に結果のメールがスマートウォッチに来た。
かると思ってなかったので嬉しすぎてその日は授業に身が入らなかった。
去年の発表は6/14だったので今年もそろそろですね。
/* この日からはtwitterとかで「セキュキャン」とか「セキュリティ・キャンプ」とかで検索しないほうが良い。 妬みは非常に見苦しいのでやめましょう */

前日

正直当日起きれるか微妙だったので夜行バスで東京に行くことにした。
しかし、夜行バスに乗り遅れて死

1日目

早朝に新宿に到着し時間まで東京をふらつく。
(当時流行っていたアニメ「NewGame!!」の聖地巡礼で阿佐ヶ谷へ行ったり秋葉原行ったりしてました)

時間ギリギリになりつつも会場に到着し名刺交換等…

3日目

寝坊した。
運営からのモーニングコール(絶望)で飛び起き、急いで支度して部屋を出ようとしていたところに運営の人が部屋まで来た。

5日目

貰いました

修了証書授与の時に「眠い?」って聞かれた。(大丈夫です。ちゃんと起きてます。) このあと適当に東京ふらついて赤羽の宿へ

X日目(コミックマーケット90)

二回目のコミケ参加。

セキュリティキャンプ関係者のサークル等を廻る。
その後新幹線で帰路へ

グループ「おねぇちゃぁん(裏声)」の人たちへ

クソ無能で本当に申し訳ありませんでした

BrainFu*kインタプリタを書いた

この記事は#一人GWハッカソン 5/3の記事です

今日のテーマはなんかのインタプリタを作るです

最初は自分で言語作る気満々でした

11:00~12:00 言語仕様の妄想

  • 四則演算ができる
  • スタックが用意されていて自由に使える
  • 変数は予め定められた8つ
  • 配列なんてなかった
  • 標準で用意されている関数はprintのみ
  • if,while,breakが使える
  • for文なんてなかった

12:30~13:55

AbemaTVで「変態王子と笑わない猫。」一挙放送やってたので見ました。
脳が溶けました。

13:00 ~ 14:00 感覚でコードを書き始める

javascriptで作った四則演算のプログラムを参考にprintとかを実装し始めるものの、
途中でわけわからなくなって死。

14:00 ~ 調査

流石に感覚で書くには無理があると感じ、ネットの情報を漁り始める。
一般的なインタプリタでは字句解析と構文解析というものが行われているらしい。
そして構文解析によって生成された構文木を元にプログラムが実行される。
なるほどなるほど。構文木とかどうやって実装したら良いのか全くわからないんですけど…ウケる…

15:00~16:00 (:3 」∠ )

17:00~ BrainがFu*ck

結局何もできてねーなとか思いながら微妙な気分になる。
何も成果物が無いのは流石にアレなので最終手段として考えていたBrainFu*ckを書き始める…

BrainFuckの実装は[,]さえ実装してしまえば後は簡単だった。

BrainFu*k f:id:akkkix:20170504084158j:plain

なんの捻りもないただのBrainFuc*kが完成した。

反省…

昔四則演算のプログラム書いたことあるしいけると思ってた 実装力がなくてつらいなぁ…
GW後半らへんにネタ切れになったらまた挑戦したい…

参考:

Brainfuck - Wikipedia

http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/

プログラミング言語を作る

四則演算(昔書いたやつ。再帰下降構文解析っていうらしい。):

sisokuenzan

OSなしでHelloWorld

この記事は#一人GWハッカソン 5/2の記事です

本日のテーマはOSなしでHelloWorldをするです

最初はOSを作ろうと思っていましたが一日では無理でした。

10:00~12:00 環境構築

Windowsで環境を構築しようとしたけど上手くいかない… 仕方なくVirtualBoxの上のLinuxを使用する

OSはXubuntu16.04、アセンブラはnasm、仮想マシンにはqemuvirtualboxを使用

12:00~13:00 初めてのプログラム

 [BITS 16] 
ORG 0x7C00
BOOT: 
CLI
HLT 
TIMES 510 - ($ - $$) DB 0
DW 0xAA55

起動した後にブートセクタがメモリの0x7C00に読み込まれるらしいのでORGで0x7C00をセット(GASではORGは使えないみたいですね)
CLIで割り込み禁止、HLTでプログラム終了
最後に位置合わせに0x00を出力しブートセクタ終端の0xAA55
C言語でいう

#include ‹stdio.h›
int main(void){
   return 0;
}

みたいなプログラムらしい。

13:00~14:00 いろいろ

スタックとかセグメントとか

14:00~15:00 INT10

画面入出力とかディスクの読み書きとかするためにはBIOSが提供している機能を使う
画面入出力をするためにはINT 0x10を使う。

INT0x10を使って文字を出力するためには
AHに0x0E
ALに文字のASCIIコード
BHにビデオページ番号
(よくわからないけど0x00で良いらしい)
BLに色を指定

 [BITS 16] 
ORG 0x7C00
BOOT: 
CLI
MOV AH, 0x0E
MOV AL, 0x41 ;'A'
MOV BH, 0x00
MOV BL, 0x07 ;グレー
INT 0x10
HLT 
TIMES 510 - ($ - $$) DB 0
DW 0xAA55

色が変わらないと思ったらビデオモードというのを変えないといけないらしい
グラフィックモードを指定するためにはAXに0x4f02を入れてBXにビデオモードを入れる ビデオモードは VESA BIOS Extensions - Wikipedia で解説されている

 [BITS 16] 
ORG 0x7C00
BOOT: 
CLI
MOV AX, 0x4f02
MOV BX, 0x0102 ;800x600 16色モード
INT 0x10

MOV AH, 0x0E
MOV AL, 0x41 ;'A'
MOV BH, 0x00
MOV BL, 0x0A ;ライトグリーン
INT 0x10
HLT 
TIMES 510 - ($ - $$) DB 0
DW 0xAA55

AHに0x13を入れてINT 0x10すると文字列が表示できるらしいです f:id:akkkix:20170503100750j:plain
なんかちょっと感動

15:00~16:00 寝

昼寝

16:00~ ゚Ω\ζ°)チーン

ディスクの読み書きにハマる
解決方法が分からず終

反省とか

またいつか挑戦したい
こんな感じであと5日浅く広く色々やれればと思う
INT0x10しただけだしハッカソンじゃなくね!?

参考:

http://softwaretechnique.jp/OS_Development/scratchbuild.html

https://en.wikipedia.org/wiki/INT_10H

VyOSでルーターを作る

お久しぶりです。

訳あって家のネットワークを分ける必要がでてきたので、今日はVyOSを使ってルーターを作ってみます。

VyOSとは

今日の目標

トポロジ
f:id:akkkix:20170127173204j:plain
いわゆる2重ルーターです

ファイアウォール

送信元 送信先 操作
192.168.2.0/24 192.168.1.0/24 DROP

ポート開放

受信ポート 送信先 送信ポート
1234 192.168.2.2 22

インストール

https://vyos.io/
stableをダウンロードしました。(v1.1.7)

イメージから起動し、ログインします。

install image

インストールが始まるので少し待ちましょう。

インターフェイスへのIPアドレスの割当

インストールが終わったら、起動させてログインします。

configure

設定モードへ入ります。
eth0を192.168.1.2/24、eth1を192.168.2.1/24にします。

set interfaces ethernet eth0 address 192.168.1.2/24
set interfaces ethernet eth1 address 192.168.2.1/24

NATの設定

外部側がeth0、内部側がeth1です。

set nat source rule 1 outbound-interface eth0
set nat source rule 1 source address 192.168.2.0/24
set nat translation address masquerade

これで192.168.2.0/24内のホストがNATされて192.168.1.0/24側に出られるようになりました。

デフォルトゲートウェイの設定

このままでは192.168.2.0/24側のホストはインターネットに出られません。
次に経由するルーター(トポロジのPR-S300NE)を指定します。

protocols static route 0.0.0.0/0 next-hop 192.168.1.1

※systemのgateway-addressでも設定できるみたいです

set system gateway-address 192.168.1.1

ファイアウォールの設定

今回は192.168.2.0/24から送信されたパケットの宛先が192.168.1.0/24だった場合のみDROPします。

set firewall name private-public default action accept
set firewall name private-public rule 1 destination address 192.168.1.0/24

お好みでログを設定します。

set firewall name private-public rule 1 log enable

インターフェイスファイアウォールを設定します。

set interfaces ethernet eth1 firewall in name private-to-public

ポート開放

内部のホストにsshできるようにしておきます。

set nat destination rule 1 inbound-interface eth0
set nat destination rule 1 protocol tcp
set nat destination rule 1 destination address 192.168.1.2
set nat destination rule 1 port 1234
set nat destination rule 1 translation address 192.168.2.2
set nat destination rule 1 port 22

2重ルーターになっているのでインターネットからアクセスしたい場合はインターネットに接続しているルーターもポート開放します。

VyosにSSHで接続できるようにする

面倒なのでSSHでどこからでもVyOSを設定できるようにします。

set service ssh port 22

NTPの設定

ログ等に正確な時間が記録されるようにVyOSをNTPで時刻合わせします。

set system ntp server ntp.jst.mfeed.ad.jp

DNSIPアドレスの設定

DHCP等は特に設定していないのでホスト名を解決できるようにDNSを設定します。

set system name-server 8.8.8.8

設定の反映と保存

設定を変更したら忘れずに

commit
save

やりましょう。

静的ルーティングの設定

最後に192.168.1.0/24から192.168.2.0/24にアクセスできるように静的ルーティングを設定しておきます。
(PR-S300NEの例)
f:id:akkkix:20170127184035j:plain

まとめ

ネットワークの勉強がてらVyOSでルーターを作ってみましたが案外簡単に設定できて拍子抜けでした。
BGPやOSPF等にも対応しているそうなので別の機会にそちらも試してみたいです。
わざわざ高いルーターを買わなくてもこのような形で勉強できるのは素晴らしいと思います。