會嘗試使用C#撰寫模擬器,有很多因素,
一方面這是我工作主要使用的語言,
二方面想看看 C# 是否能勝任模擬器的工作,
三方面是希望撰寫出方便教學與理解.移植的程式,不像C/C++很多地方與OS.硬體特性.函式庫有關係上的相連,如果移植有些地方都要花心思磨合一下,這也就是為何我盡量以.NET C#原生官方的資源為主,而沒使用到像是DirectX之類的第三方原件或是直接call win32 api等等做法(不過遊戲搖桿讀取有用到sharpdx,內部是包directx利用的).
這樣搞一搞之後,以現在普通的電腦來說,如果程式寫得還算適當,並且避開某些地雷寫法(C#某些物件導向的高階元件千萬別用,一切用最簡單的方式去做),然後在處理畫面部分,直接用指標去做處理(C#的BitMap物件很慘...一定得用指標直接去讀寫pixel value),整體跑起來其實也沒啥問題 (比較早期的2D遊戲主機,像是GB.GBC.FC.SFC.GBA等等之類的....) , 但這就僅止於跑起來ok而以,如果再加上像是畫面用filter倍率放大,一套用上比較複雜的演算法下去,fps馬上掉到60fps內,甚至一半都不到....
也就是說c# ok ,但僅止於ok而以.
如果真的要求完善完美,不然就是得call native,不然就是得借用dirextx wRapper直接使用硬體加速(GDI真的不適合當成動態畫面播放的媒介,特別是圖片大張時候,記憶體量一大,整個顯示速度就死翹翹),而放大即時的filter運算,c#也很不夠力....
所以c# ok ,但求完美部分關鍵得借用d2d.win32 api或是直接call c method去處理,有沒有發現一個問題? 如果這樣做,那不就跟當初理念違背? 如果跟os.硬體綁上,用c.c++不是更快? 恩...這的確就是一個矛盾的地方.
最後歸納起來,會用c#的原因,大概就最主要是funny和熟悉吧....
如果一開始追求一個寬裕的效能,而不僅止於ok的話,c/c++會是更好的選擇,前提是你熟習它們.
目前效能障礙在
1.real time image filter (如果是hq2x這套,光2x就死翹翹....)
2.bitmap投影速度 (GDI圖越大張,速度越慢)
有些c/c++的專案還會用組語更進階加速.
2015年10月18日 星期日
2015年10月12日 星期一
Linear feedback shift register,LFSR 模擬 white noise 輸出....
關於 線性反饋移位暫存器 介紹如下....
https://zh.wikipedia.org/wiki/%E7%BA%BF%E6%80%A7%E5%8F%8D%E9%A6%88%E7%A7%BB%E4%BD%8D%E5%AF%84%E5%AD%98%E5%99%A8
要先大概知道這是啥....
http://belogic.com/gba/channel4.shtml (雖然這以gameboy advance為教學,但這塊觀念部分與gb互通,實際上gba的sound系統有部分就是沿用gb硬體....)
總之gameboy sound channel 4 用來產生 "模擬" white noise 的輸出,什麼是white noise ??
https://en.wikipedia.org/wiki/White_noise
最簡單的結論就是white noise可以想像成一堆聲音的雜訊,像是廣播沒調準頻道或是傳統電視沒調整到有效頻道所發出的ㄘㄘ聲就是white noise的表現...可視為無規則性亂數產生的雜亂訊息.
這在gb上是channel 4的表現效果,不過gb的white noise並不是真正的完全的隨機亂數(如何用公式或一定的規則算法輸出亂數,滿高深的數學問題...,這邊是用硬體作簡單模擬輸出),而是利用LFSR產生的模擬輸出,由於離開學校久了...剛開始看這塊,真的看得一頭霧水,查了一些資料想了想後,才大概搞清楚這輸出的主要規則,但整個GB SOUND輸出模擬,還有很多地方搞不太懂(跟畫面同步的觀念和模式? 頻率與真正輸出取樣無法整倍數? 超過一般CD 44.1kHZ的超高頻是要怎樣處理?? ),這篇先整理這塊心得.
簡單來說就是 CHANNEL 4 有兩個輸出 模擬亂數的參數, 用 7個 或是 15個暫存器位移記算,來產生模擬亂數續列(之所以說模擬,是因為它的輸出其實是會循環的,不過之後都簡化成亂數輸出稱呼).
先以 7個暫存器的說明範例....
7個暫存器的狀態可以想像成7個連續的bits , bit 7 ~ bit 1 (最低位這邊以bit 1稱呼) ,初始化每個bit都為1,所以為1111111,每一次輸出亂數的過程經過底下幾個步驟
1.輸出最右端的bits當成亂數輸出續列
2.資料右移 >> 1 位,
3.最高位以 bit 1 與 bit 2 的 exor 運算結果填補
4.得到新的 7bits 序列 , 回去 步驟一循環運作
下面C#部分的範例code
byte LSFR = 0x7f; //init 1111111
for (int i = 0; i < 1024; i++) //執行 1024次
{
string b_str = Convert.ToString(LSFR, 2).PadLeft(7, '0');
listBox1.Items.Add(b_str + " " +LSFR.ToString()); //
byte b1 = (byte)(LSFR & 1);
byte b2 = (byte)((LSFR & 2) >> 1);
LSFR >>= 1;
LSFR |= (byte)((b1 ^ b2) << 6);
}
不過channel 4 , 我還有頻率對應輸出的規則沒搞懂...先這樣.
原本以為channel 4 應該是最好k的部分,相比下其實前面1.2.3還容易點.
印象中以前學生時候有學到LFSR這東西,但現在都忘光光了,補回,
ps.LFSR有各種實作的方式,大概都大同小異,這邊僅針對GB硬體的方式來處理,包括像是幾個暫存器,初始值.用哪2個位元來做EXOR填補都可以是變化之一.
比起模擬器cpu.gpu.mem等等實作,我覺得apu聲音輸出這塊對一般人來說最難搞懂,也最難完全實做正確.
https://zh.wikipedia.org/wiki/%E7%BA%BF%E6%80%A7%E5%8F%8D%E9%A6%88%E7%A7%BB%E4%BD%8D%E5%AF%84%E5%AD%98%E5%99%A8
要先大概知道這是啥....
http://belogic.com/gba/channel4.shtml (雖然這以gameboy advance為教學,但這塊觀念部分與gb互通,實際上gba的sound系統有部分就是沿用gb硬體....)
總之gameboy sound channel 4 用來產生 "模擬" white noise 的輸出,什麼是white noise ??
https://en.wikipedia.org/wiki/White_noise
最簡單的結論就是white noise可以想像成一堆聲音的雜訊,像是廣播沒調準頻道或是傳統電視沒調整到有效頻道所發出的ㄘㄘ聲就是white noise的表現...可視為無規則性亂數產生的雜亂訊息.
這在gb上是channel 4的表現效果,不過gb的white noise並不是真正的完全的隨機亂數(如何用公式或一定的規則算法輸出亂數,滿高深的數學問題...,這邊是用硬體作簡單模擬輸出),而是利用LFSR產生的模擬輸出,由於離開學校久了...剛開始看這塊,真的看得一頭霧水,查了一些資料想了想後,才大概搞清楚這輸出的主要規則,但整個GB SOUND輸出模擬,還有很多地方搞不太懂(跟畫面同步的觀念和模式? 頻率與真正輸出取樣無法整倍數? 超過一般CD 44.1kHZ的超高頻是要怎樣處理?? ),這篇先整理這塊心得.
簡單來說就是 CHANNEL 4 有兩個輸出 模擬亂數的參數, 用 7個 或是 15個暫存器位移記算,來產生模擬亂數續列(之所以說模擬,是因為它的輸出其實是會循環的,不過之後都簡化成亂數輸出稱呼).
先以 7個暫存器的說明範例....
7個暫存器的狀態可以想像成7個連續的bits , bit 7 ~ bit 1 (最低位這邊以bit 1稱呼) ,初始化每個bit都為1,所以為1111111,每一次輸出亂數的過程經過底下幾個步驟
1.輸出最右端的bits當成亂數輸出續列
2.資料右移 >> 1 位,
3.最高位以 bit 1 與 bit 2 的 exor 運算結果填補
4.得到新的 7bits 序列 , 回去 步驟一循環運作
下面C#部分的範例code
byte LSFR = 0x7f; //init 1111111
for (int i = 0; i < 1024; i++) //執行 1024次
{
string b_str = Convert.ToString(LSFR, 2).PadLeft(7, '0');
listBox1.Items.Add(b_str + " " +LSFR.ToString()); //
byte b1 = (byte)(LSFR & 1);
byte b2 = (byte)((LSFR & 2) >> 1);
LSFR >>= 1;
LSFR |= (byte)((b1 ^ b2) << 6);
}
不過channel 4 , 我還有頻率對應輸出的規則沒搞懂...先這樣.
原本以為channel 4 應該是最好k的部分,相比下其實前面1.2.3還容易點.
印象中以前學生時候有學到LFSR這東西,但現在都忘光光了,補回,
ps.LFSR有各種實作的方式,大概都大同小異,這邊僅針對GB硬體的方式來處理,包括像是幾個暫存器,初始值.用哪2個位元來做EXOR填補都可以是變化之一.
比起模擬器cpu.gpu.mem等等實作,我覺得apu聲音輸出這塊對一般人來說最難搞懂,也最難完全實做正確.
2015年10月5日 星期一
JVM 心得二 從arduino 談起
目前移植了非常精簡化的JVM版本到ARDUINO,其實只能算是一種技術概念驗證,不是什麼完整的東西,只實現了幾個opcode的指令集,大概足夠跑跑走馬燈範例,然後一些method呼叫native去達成,程式非常地小巧,目前只是個人嘗試,不算完整發展的專案,如果日後有發展的熱忱在,這東西應該會變成是一個可能在SD卡模組上動態載入程式的方式.
其實在arduino上跑jvm不太有啥好處,真正說來我覺得好處大概只有程式碼在單一大小於一定size前(至少一份要能夠載入運作),可以把程式切割成很多部分,放在SD卡上,隨時可以動態載入程式或是切換,達到某種SWAP的功能,而且變成也不需要燒錄步驟.
目前這樣的做法最大的問題其實是跟native call的對應,目前是依照範例對應寫死
https://123d.circuits.io/circuits/1074042-arduino-jvm
但這native call的index其實都不一定相同,這介面階口在轉換時要想辦法動態轉接一下.
關於這些以後有新的發展後再報告了.....
其實jvm這類stack machine有一個優點應該是說美感是我很欣賞的...那就是核心架構概念非常精簡,靠一個stack和一個array加上少少幾個opcode,就可以變化出許許多多的變化,如果把java 物件導向和比較複雜的部分去除,這stack vm真的相當好實作與移植,java的jvm複雜在物件導向相關功能的記憶體模型概念,但OPCODE的執行確相當簡單扼要....到有一種極簡的美感.
這類虛擬機雖然號稱跨平台,但這有很多前提.....在純運算邏輯處理的部分,到哪裡跑都一樣,但跟OS有關係.跟IO有關係的部分,它的呼叫就一定會用到原生的功能,到哪都可以跑的前提是,至少在原生呼叫的階口都保有相同的特性和完整實作,否則都是會出問題的.
其實在arduino上跑jvm不太有啥好處,真正說來我覺得好處大概只有程式碼在單一大小於一定size前(至少一份要能夠載入運作),可以把程式切割成很多部分,放在SD卡上,隨時可以動態載入程式或是切換,達到某種SWAP的功能,而且變成也不需要燒錄步驟.
目前這樣的做法最大的問題其實是跟native call的對應,目前是依照範例對應寫死
https://123d.circuits.io/circuits/1074042-arduino-jvm
但這native call的index其實都不一定相同,這介面階口在轉換時要想辦法動態轉接一下.
關於這些以後有新的發展後再報告了.....
其實jvm這類stack machine有一個優點應該是說美感是我很欣賞的...那就是核心架構概念非常精簡,靠一個stack和一個array加上少少幾個opcode,就可以變化出許許多多的變化,如果把java 物件導向和比較複雜的部分去除,這stack vm真的相當好實作與移植,java的jvm複雜在物件導向相關功能的記憶體模型概念,但OPCODE的執行確相當簡單扼要....到有一種極簡的美感.
這類虛擬機雖然號稱跨平台,但這有很多前提.....在純運算邏輯處理的部分,到哪裡跑都一樣,但跟OS有關係.跟IO有關係的部分,它的呼叫就一定會用到原生的功能,到哪都可以跑的前提是,至少在原生呼叫的階口都保有相同的特性和完整實作,否則都是會出問題的.
訂閱:
文章 (Atom)