HTML5を使ってみた
2012年10月5日
去年がHTML5元年だそうで、HTML5はまだ始まったばかりの技術なのだけれど、モバイル環境でも使えるアプリケーションを開発できるという意味ではおもしろい。
最近、MZ-80K(SHARPが30年少し前に発表したパソコン)のレプリカを作ったり、それ用のBASICを開発したりで、これに少しはまりこんでいる。
そこで、出来て1年ほどの技術を用いて30年以上前のコンピューターを再現するのもおもしろかろうと思い、HTML5の勉強を兼ねてMZ-80Kのエミュレーターを作ってみた。「KM-Z80 web」と名付けている。
現物及び使用方法の説明、ソースコードのダウンロードなどはこちら
使い方などは別のページに譲るとして、ここではHTML5の技術に関することなどを書いてみたい。
HTML5のうち、利用した技術は次の通り。
FileReaderでは、動的HTMLページを用いなくても、クライアントサイドのファイルをデーターとしてJavaScriptの中に取り込むことが出来るのが面白い。この特性を利用して、KM-Z80 webは、静的なHTMLファイルとして構築することが出来た。セキュリティーに関してはブラウザ側に丸投げすることが出来るので、応用範囲は広いだろうと思う。ここでは、以下のように使っている。
最後は音源関連。これは残念ながら、対応しているブラウザが限られており、ブラウザごとにAPIが異なる。今のところ、FireFoxとChromeで動作確認をしているが、それぞれ、Audio Data API と Web Audio API という、2つの異なるAPIを使うことになった。詳しくは、ソースコードのaudio.jsを参照されたい。MZ-80Kの場合は矩形波で良いので、そこの部分は楽にプログラミングできた。
ところで、このKM-Z80 webには、これ用にスクラッチから開発した、JavaScriptによるZ80エミュレーターが搭載されている。以下、ソースコード(Z80.js)から。
使い方としてはわかりやすくできていると思うので、興味のある方はどうぞ。LGPLです。
最近、MZ-80K(SHARPが30年少し前に発表したパソコン)のレプリカを作ったり、それ用のBASICを開発したりで、これに少しはまりこんでいる。
そこで、出来て1年ほどの技術を用いて30年以上前のコンピューターを再現するのもおもしろかろうと思い、HTML5の勉強を兼ねてMZ-80Kのエミュレーターを作ってみた。「KM-Z80 web」と名付けている。
現物及び使用方法の説明、ソースコードのダウンロードなどはこちら
使い方などは別のページに譲るとして、ここではHTML5の技術に関することなどを書いてみたい。
HTML5のうち、利用した技術は次の通り。
- Canvas
- FileReader
- Web Audio API もしくは Audio Data API
display.init=function(){ // Set the contexts. this.context=document.getElementById("display").getContext("2d"); this.ledContext=document.getElementById("led").getContext("2d"); // Load the font data. See also fonts.onload event. this.fonts.src="./fonts.png?"+ new Date().getTime(); }; display.fonts.onload=function(){ display.onload(); } // display.onload will be called after sucessfull loading of font PNG image. display.onload=function(){ // Show the PNG image this.context.drawImage(display.fonts,0,0); // Construction of images for font for (h8=0;h8<16;h8++) { for (l8=0;l8<16;l8++) { this.font[h8*16+l8]=this.context.getImageData(l8*8,h8*8,8,8); } } // Construction of images for LED this.ledImage[1]=display.context.getImageData(128,0,16,16); this.ledImage[0]=display.context.getImageData(128,16,16,16); // Clear display var i; for (i=0;i<1000;i++) { this.write(i,0); } // Construct blank canvas var blank=document.getElementById("blank"); blank.getContext("2d").putImageData(this.context.getImageData(0,0,320,200),0,0); // Show LED (green) this.led(1); // All done let's start Z80 start(); }; display.write=function(addr,data){ var posy=0; var posx=addr & 0x3ff; if (1000<=posx) return; while(40<=posx) { posy++; posx-=40; } this.context.putImageData(this.font[data],posx*8,posy*8); }; display.led=function(green){ this.ledContext.putImageData(this.ledImage[green?1:0],0,0); }要するに、次のような画像をキャンバス上に貼り付けて、その一部を256回、フォントデーターとして取り込んで保存(getImageData)している。表示するときはputImageDataを用いれば、高速に処理がなされる。
FileReaderでは、動的HTMLページを用いなくても、クライアントサイドのファイルをデーターとしてJavaScriptの中に取り込むことが出来るのが面白い。この特性を利用して、KM-Z80 webは、静的なHTMLファイルとして構築することが出来た。セキュリティーに関してはブラウザ側に丸投げすることが出来るので、応用範囲は広いだろうと思う。ここでは、以下のように使っている。
file.loaded=function(obj){ // This will be called when a file is uploaded. // If FileReader API is not supported, following code will fail. var fr = new FileReader(); fr.onload = function () { var data=new Uint8Array(fr.result); file.update(data); }; fr.readAsArrayBuffer(obj.files[0]); obj.style.display='none'; }; file.update=function(data){ // This will be called when a file is sucessfully loaded by FileReader API. var header=new Array(); var body=new Array(); var i; // First 128 bytes contain header info. for (i=0;i<128;i++) { header[i]=data[i]; }ここでは、バイナリファイルを扱うので「readAsArrayBuffer」を用いた少し複雑なコードになっているが、テキストでよいのであれば、「readAsText」を用いてもう少し簡単に記述できるようだ。あと、これはHTML5ではないのだろうけれど、以下のようなリンクを作成することで、バイナリオブジェクトをローカルに保存することを可能にしている。
<a href="data:application/octet-stream,%00%01%02">download</a>
最後は音源関連。これは残念ながら、対応しているブラウザが限られており、ブラウザごとにAPIが異なる。今のところ、FireFoxとChromeで動作確認をしているが、それぞれ、Audio Data API と Web Audio API という、2つの異なるAPIを使うことになった。詳しくは、ソースコードのaudio.jsを参照されたい。MZ-80Kの場合は矩形波で良いので、そこの部分は楽にプログラミングできた。
ところで、このKM-Z80 webには、これ用にスクラッチから開発した、JavaScriptによるZ80エミュレーターが搭載されている。以下、ソースコード(Z80.js)から。
/* Public methods: z80.reset(); z80.setSpeed(clk); z80.getMicroSec(); z80.exec(msec); z80.interrupt(); z80.nmr(); Required outside methods: Methods written in z80functions.js. memory.read(addr); memory.write(addr,data); io.read(addrL,addrH); io.write(addrL,addrH,data); z80.events(); // optional */
使い方としてはわかりやすくできていると思うので、興味のある方はどうぞ。LGPLです。