2017年1月20日 星期五

6502 的 dummy read 與 write

這種行為是光看功能spec不會發現的現像,簡單來說反正就是不知道這顆cpu是怎樣去設計的,在讀取時候,指令對於資料讀寫的目標位址分成高低各1byte組成,如果 低byte發生carry (cross page) , 會產生多一次的讀取或是寫入行為(所以指令的cycle也會多出來) , 對於一般的記憶體相同資料寫入兩次或是讀取兩次,並沒有任何影響性,但任天堂紅白機記憶體區域有部分帶有 buffer或是latch的特性,特別是一些register io的 io mapping 區域,這時候就得針對這種奇怪的行為做處理,至於指令規格描述單就功能層面並不會提到這回事.

其實紅白機真的有很多奇奇怪怪的硬體特性,有些部分像是秘技一樣,早期前輩先開路摸索,現在這些秘密多半已經公開文件化,還有一些為了測試正確性而開發的ROM給模擬器開發者測試,雖然已經不用再像以前直接測試硬體做功能測試,但看那些描述文件或是網路資料常常也不是非常簡單理解就是.


2016年12月12日 星期一

C#使用指標雜談

C#使用指標並不是什麼很厲害的技術,新手除外,稍微對C#有更深入一些經驗的人,我猜多數都有在C#使用指標的經驗,所以我這邊主要也不是要介紹C#使用指標這件事情,而是雜談些別的.

C#雖然可以使用指標,但如果是一般的方式
https://msdn.microsoft.com/zh-tw/library/f58wzh21.aspx
也就是搭配 fixed 陳述式 , 跟 C 指標操作存取相比會顯得非常沒彈性 , 所以在C#達不到C操作指標的靈活度.......但如果使用 Marshal 來進行 unmanaged 的記憶體配置拿到指標 , 則在C#中則"幾乎"(還有不少確認和爭議空間)可以達到 C 操作指標的靈活度

Marshal.AllocHGlobal
Marshal.FreeHGlobal

這兩的東西好用至極,之後要存取物件的習慣也幾乎跟C一樣,詳細的使用介紹就自行google,不多談.

這邊要說到的是有了這項能力後,處理模擬器其中一個問題顯然簡單而且有效率很多,電腦世界中雖然最基本的資料單位是 bits , 但硬體上資料能存取的最基本單元是 bytes , 8bit CPU 要模擬記憶體的硬體機本上宣告一個固定大小以byte為資料型態的array即可 , 不過到了32bit CPU一次存取可能就是 half-word (2bytes) 或是 word (4byte) , 有時候你從一個位址讀資料可能是要讀一個 byte , 有時候可能是一個 ushort , 有時候可能是一個 uint .



如果要讀4個byte, 你可以自己對 byte array 讀取4次  , 然後用 bitwise 的操作自己串成一個 uint ,顯然是麻煩 , 使用指標的狀況下 ,你可直接指定說我要從這指標的某個offset讀取 byte ushort uint 位元長度資料.

            byte* bytepointer = (byte*)Marshal.AllocHGlobal(sizeof(byte) * 16);

            bytepointer[0] = 0x00;
            bytepointer[1] = 0x11;
            bytepointer[2] = 0x22;
            bytepointer[3] = 0x33;
            bytepointer[4] = 0x44;
            bytepointer[5] = 0x55;
            bytepointer[6] = 0x66;
            bytepointer[7] = 0x77;
            bytepointer[8] = 0x88;
            bytepointer[9] = 0x99;
            bytepointer[10] = 0xaa;
            bytepointer[11] = 0xbb;
            bytepointer[12] = 0xcc;
            bytepointer[13] = 0xdd;
            bytepointer[14] = 0xee;
            bytepointer[15] = 0xff;

            ushort t_us;
            t_us =   ((ushort*) &bytepointer[4])[0];
            Console.WriteLine(t_us.ToString("x4"));
            t_us = ((ushort*)&bytepointer[9])[0];
            Console.WriteLine(t_us.ToString("x4"));

            uint t_ui;
            t_ui = ((uint*)&bytepointer[2])[2];
            Console.WriteLine(t_ui.ToString("x8"));
            t_ui = ((uint*)&bytepointer[4])[1];
            Console.WriteLine(t_ui.ToString("x8"));
            t_ui = ((uint*)&bytepointer[9])[0];
            Console.WriteLine(t_ui.ToString("x8"));
            Marshal.FreeHGlobal((IntPtr)bytepointer);

資料存取變得像切豆腐一樣容易,對於資料長度讀取的轉換可以任意有彈性來做.

不管是Read 8/16/32 或是 Write 8/16/32 ,把資料長度型態改變, 對可以對應了.

2016年12月6日 星期二

移植雜談

測試了一下網路上別人寫出的ntsc掃描線模擬函庫,出來的效果完全是我的想要的效果,太完美了,嘗試實作自己的一套,但要去理解ntsc畫面訊號規格太複雜,至少對我來說需要不少時間,於是打算使用移植的方式,從C移植到C#去,最後C#整個都在跟指標打交道,出現照理來說C#不常看到的 -> & 等等符號...但其實從C/C++移植最難的反來不是指標(甚至照理來說應該要把指標的處理方式給改寫掉比較符合風格上的正確),而是寫C的人很習慣把玩巨集和一些前置條件,而寫C++寫到狂的人則超愛用一堆複雜的樣板到讓人有走火入魔的感覺,還有一堆詭異C++近年來的新語法規格,這些移植到C#都相當麻煩.

2016年11月19日 星期六

Win3mu

作者解說
https://hackernoon.com/win3mu-part-1-why-im-writing-a-16-bit-windows-emulator-2eae946c935d#.ca6ygh8cl

專案發佈站
http://www.win3mu.com/

可以在現在64位元 win10環境下跑以前 win3時代16bit的應用軟體.

作者手法相當有趣.

cpu是撰寫出來模擬的,但跟api服務有關的部分全都靠remp到64位元系統相似api去吃.

由 upernes 談起...

http://blog.vreemdelabs.com/2016/05/08/upernes-a-nes-to-supernes-game-recompiler-passing-the-tests/


這專案的概念其實有相當多的討論議題延伸,大概簡單說一下, nes 主機用的是 6502 , snes 用的是 65c816 , 其實可以視為是 6502的16bit功能延伸版,本身其實有設計8bit相容模式,所以曾傳聞超級任天堂有考慮過向下相容任天堂紅白機遊戲,但因為cost考量取消這相容能力....

到這邊大家會想到,那既然如此有沒有可能將任天堂的遊戲放到超任上跑,透過某些轉換的方式?

理論上當然可以,但實際上問題當然很多....程式能否執行cpu相容的問題其實只佔部分比例,還有很多層面的問題要考量,光snes硬體的memory map 跟 gpu 等等架構就跟 nes 不同了, 光cpu指令理論上可以達成相容互通又如何??

這專案的確示範了某種概念,但發展有限,可以執行很簡單的demo,估計是做了很多 remap 的工作.

而像是任天堂的GBA或是NDS用的是ARM系列CPU,現在手機很多也多數是用ARM處理器,當然隨著ARM版本不同,指令集會有部分差異,所以理論上在ARM環境上最高效率的執行ARM遊戲主機遊戲的方式應該是透過一個轉換器,針對少部分指令的差異重編達成相容,然後只需要模擬CPU以外的硬體環境即可,但想也可以知道理論上好像越簡單的東西,實作上問題就是更多,目前也沒有看過任何人發展出這種方式來執行.

目前也只有看過把NES的ROM透過LLVM工具編譯成執行檔在X86環境上執行.

簡單來說,能否執行光只靠CPU能互通這點條件是不足的.

由xBRZ filter淺談平行處理問題

xBRz是目前我用過在cpu cost與畫質上cp值頗高的影像放大濾鏡演算法
https://sourceforge.net/projects/xbrz
原始版本是C++ , 有人移植到C# ,我再接手進行一些效率優化的處理以及把一些最新xBRZ C++官方版本的最新功能加入,至少套用在電玩畫面的高效率輸出,即使用C#來寫效率也是OK的 .

但這演算法有一套很可惜的地方,導致移植到平行處理的方式會有問題,只能用某些折中的方式來進行平行多核處理.

首先這套演算法有每一條平行線與平行線之間的計算順序上的前後相依性,而且目前我還沒辦法找到方式去改寫這塊,因為這似乎就是xBrz演算法其中必須的核心精神要項之一,要拆除順序相依的問題,可能得自己研發變種版xBRZ.

因為這種相依性,平行計算下去,在計算順序無法控制預期的狀況下,出來的結果就是亂七八糟的.

目前找到兩套解法各有優缺點.

1.把畫面切成4大塊,4大概各自平行處理,效率滿好的,缺點只有兩個,一個是四塊區域銜接的邊界其實運算結果有可能是錯誤的,但因為其實原始只佔一pixl的誤差,一般沒很刻意去注意和比較根本不可能查覺,二是目前有太多電腦cpu核心數量其實超過4核,把整個運算切成4份,其實還浪費掉其他閒置率高的cpu.

2.跟線與線有關的順序性結果先算好出來,這部分沒平行加速幫忙,因為也幫不上忙,剩下再用平行的處理方式下去跑 (高的長度處理迴圈用parallel) , 缺點當然是有部分的計算是單核跑,且跟第一個方法比起來,context switch的cost當然也高不少(這還有改善空間就是,未必要以高度數量來當工作數量),優點來說的話就至少算出來的畫面是沒有瑕疵的,且cpu核心至少在處理高度迴圈時是有整個盡用的.

像這種順序相依問題一但拆解不掉,拿到GPU去跑一樣無法,甚至更慘,以前曾想過拿GPU來算一些結果,特別是畫面的計算這塊,但後來發現結果沒比CPU好,好壞結果真的很難論,最後未必有絕對性的優勢下,考慮利弊拿捏,最後沒往那方向走.

先撇開相依性的問題不談,假設一個龐大的計算工作,可以同時拆解成很多小任務丟給GPU跑,光是把計算資料COPY到GPU記憶體,和把GPU算好結果COPY回一般記憶體這段這段COST就把優勢佔光了....還不包括GPU計算初始化時間....這種問題,比較新的opencl加特定條件的硬體環境,似乎可以改善,總之大概就是這樣,平行處理並不是萬能仙丹,滿多問題會導致無法使用這種計算方式,即使可以算很多因素也未必說效率就一定會提高,應該是特定問題的處理適用.

至於透過網路來分散平行處理,更不用說...網路傳輸cost成本必定得計算在內.



2016年11月16日 星期三

再談任天堂紅白機撰寫心得

http://baxermux.byethost18.com/myemu/AprNes

任天堂紅白機模擬器撰寫已經到了一個段落,後續就是持續修bug和準確度修正.mapper支援追加等等工作,但由於開發目標是以學習為目的,是否要(或是說是否能夠)發展到達成專業完善水準反來是其次問題,就隨緣看興致吧.

所謂 "台上10分鐘台下10年功" 這句話放在模擬器開發上也非常適合...

願意或是不願意去嘗試撰寫,這是可能與不可能的差異.

有寫出基礎作品跟寫不出來,這是一個滿大的層次上的差異.

寫出基礎作品.跟寫出一個中等程度的作品,這是一種程度上的差異.

寫出中等程度的半熟作品與專業成熟完善作品,這可能是幾年累積差異的結果,或是神人與凡人差別.

總而言之技術和能力上的不足,只要肯用心都有可能花時間去彌補,熱情和興趣常常才是能不能堅持到最後的差異,對於平凡眾生來說.

PS.也不知道怎麼搞的,大名名鼎鼎的 NO$系列 出的NO$NES模擬器竟然連洛克人玩起來都可以出嚴重問題直接跳掉....這倒是讓我滿訝異的,洛克人3.4.6代全都屬於不能玩的階段.....但NO$網站所寫的硬體資訊確又相當詳細(雖然那種文件真的不適合初學者拿來當學習入門).