5.28.2010

Win32 Listbox Control 的操作

有時候要簡單的 log 來 show message,但是又不是 console mode ,這樣就要拉一個 ListBox 來作。

ListBox 的操作: MSDN - Listbox

首先 Win32 的 Control 就是預先寫好的小Dialog Window,所以要控制 Control Item ,只有用 SendMessage 的方式。

這種已經寫好的 Control (DialogWindow),每次要送message 過去,還要先從 ID 取得 hWnd 再送,有點麻煩,所以 Win32 提供一個直接對 ID 送 Message 的 function
SendDlgItemMessage
ListBox 的控制,用在 show log 的化,簡單的就是:
  1. 加入Item
  2. 移動 List
  3. 刪除 Item
三個都是用 SendDlgItemMesage 來完成的,三個function 的 Message ID 不一樣:
  1. 加入 Item : LB_ADDSTRING
  2. 移動List : LB_SETTOPINDEX
  3. 刪除 Item : LB_DELETESTRING
另外,ListBox 有一個 property - Sort,可以設定是否自動對 Item 排序,在作 Log 用時當然是不需要,但是 default 是 TRUE,要記得改。

ADDSTRING default 是作 "Append",所以剛好符合 log 的動作,從後面加上去。

ListBox 雖然有 scroll bar ,但是不會自己scroll 到最後,所以要用 LB_SETTOPINDEX 來指定第一個顯示的 item,好移動 scrollbar。
但是因為要讓最後一行對齊最後,所以反而要先用 LB_GETCOUNT 取得所有 item 的數量,自己減去 list 一面的 item 數好找到最後一頁的第一個 item 的 index...


以下就是簡單的 sample code,只有兩個 button - add, clear(all) 和一個 14 行的 list。
list 只保持 30 個 item,每按一下 add,就會加一行。
#include "stdafx.h"
#include <windows.h>
#include "ListBoxTest.h"


BOOL CALLBACK DialogProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
static int i=0;
switch (uMsg)
{
case WM_INITDIALOG:

return TRUE;
case WM_CLOSE:
EndDialog(hWnd,0);
return TRUE;
case WM_COMMAND:
switch(wParam)
{
case IDC_ADD :{
TCHAR outmsg[10];
swprintf(outmsg,_T("%d"),i++);

SendDlgItemMessage(hWnd,IDC_LIST1,LB_ADDSTRING,0,LPARAM(outmsg));
int cnt=SendDlgItemMessage(hWnd,IDC_LIST1,LB_GETCOUNT,0,0);
if(cnt>30)
SendDlgItemMessage(hWnd,IDC_LIST1,LB_DELETESTRING,0,0);
cnt=SendDlgItemMessage(hWnd,IDC_LIST1,LB_GETCOUNT,0,0);
if(cnt>=14)
SendDlgItemMessage(hWnd,IDC_LIST1,LB_SETTOPINDEX,cnt-14,0);
return TRUE;
}
case IDC_CLEAR:{
int cnt=SendDlgItemMessage(hWnd,IDC_LIST1,LB_GETCOUNT,0,0);
while(cnt-->0)
SendDlgItemMessage(hWnd,IDC_LIST1,LB_DELETESTRING,0,0);
return TRUE;
}
default:
return FALSE;
}
default:
return FALSE;
}
}


WTL 有包裝 ListBox,使用 WTL 的 example 是 http://realchecko.blogspot.com/2010/08/clistbox-listbox-control-in-wtl.html

5.21.2010

Change Mass Storage Volume Name/Label

變更 Mass Storage 的 Volume Name/Label

在這一篇 http://geekswithblogs.net/KMOS/archive/2010/04/17/fat-volume-and-ce.aspx
還有這一篇 http://www.tech-archive.net/Archive/WindowsCE/microsoft.public.windowsce.platbuilder/2010-04/msg00220.html

大概就是去改:public\common\oak\drivers\fsd\fatutil\MAIN\ 下面的檔案
很複雜

5.17.2010

tcpmp - sample prorgram

在 sample folder 下有比較簡單的 main sample:
把 sample_win32.c , dumpoutput.c, dumpoutput.h 拷貝到 player.c

開啟 player.sln,把原來的 source remove 掉,加入上面的兩個 .c。
build OK。

還沒測..

complian 一下 .... 無關的東西...其實是自己不好 XD

有點詞窮..
 {array Node;
...
struct node* Platform;
...}
Node 這個詞到處在用,還分大小寫..當 structure name,又當 variable name...
懷疑是不是故意的..
最好是 struct node* Platform 實際上是從 array Node 中挑出一個來的。

好像有用到 Singletone pattern,用 struct context *Context() 取出唯一的 context 指標。

用 C++寫就好,幹嘛要用C寫,然後又要做出C++的東西 (default constructer,class hierachy,function overwrite.. abstrator overwrite etc)。

用 define function 作 cast pointer 真是糟糕,看起來是 argument,卻拿來作 cast name。

然後整個 project sourcecode 就是 node 來 node 去的,什麼名字都有node,也就等於什麼名字都沒有 node...(難怪會重寫)

Module Name 先放在 string table裡,每個 id 代表一個 Module Name,然後再用 Id 來 load module。

還有..
 stringdef Def;
stringdef* Ptr = &Def;
..
(,,, &Ptr,,,)
這樣的code..

nodedef : node的定義,包含..
  • load setting
  • ID
  • parent class (的ID)
  • priority (?)
  • 這個 node (class?) 的 create function
  • delete function
ID 系統,使用 FOURCC : 四個 char (0x00~0xFF) 組成 8x4=32 bit (一個 int)。

又有node,又有 class,node define 的東西是 class。

static function,第一個 argument 在所有的caller 都是一樣的值,那為什麼不直接 reference ? 還要多一個標準?

NodeClass 就要用 FindClass 來操作。 -- 因為 FindClass 內部只對 NodeClass 這個 member 操作。(隱含的)

一堆 node, class 都 initial 完後,在 global unique variable - Context( ) 的 array xxx 就已經都註冊好所有 id 和對應的 create function。

NoteCreate( classid) 會從 context array 中找到那個 classid 的 node..
  1. LoadLibrary
  2. Call DLLRegister
  3. load parent class/node (recursive)
  4. call node 的 Create( )
所有都 Initialize 完後,call INTERFACE_ID 這個 class 起來 --- 這個 class 用 WinPopupClass( ) 叫起
又是一個 特例。所以 interface 這個 node 要用 NodeCreate( ) 出來後,要手動 cast 成 (win*) 然後取出 Popup( )

5.13.2010

code reading - tcpmp, node


NodeLoadModule( )
{
Module = LoadLibrary(path);
Proc = GetProcAddress(Module,"DLLRegister");
Result = (Proc)();
}

所有的 Node 在被 NodeLoadModule( ) load 進去的時候,Node 的 DLLRegister( ) 會被呼叫。

所以 node.h 中有宣告:

DLLEXPORT int DLLRegister(int ContextVersion);
DLLEXPORT void DLLUnRegister();

所有 作為 Node的 DLL ,都要 include node.h,所以都要 implement DLLRegister( ) 。

Node 的使用用類似 IOControl 的方式,統一一個 funciton interface : Set( ) ,再把需要的功能以 control code 傳進 Set( ) function:

Node->Set(Node,Msg,&Data,Size);

Node 的宣告是:
typedef struct node
{
int Class;
nodeenum Enum;
nodeget Get; //typedef int (*nodeget)(void* This,int No,void* Data,int Size);
nodeset Set; //typedef int (*nodeset)(void* This,int No,const void* Data,int Size);
} node;

...找不到 node 初值(內容) 設定的部份。

nod.Get, Set 設定是在 node 的 Create( ) 。
以 platform 為例:static int Create(platform* p)
{
cpudetect Info;
p->Node.Enum = (nodeenum)Enum;
p->Node.Get = (nodeget)Get;
p->Node.Set = (nodeset)Set;
p->Model = MODEL_UNKNOWN;
在 NodeRegisterClass( ) 時,該 Node的 Create( ) function 會被呼叫。

notes - code reading of ffmpeg


AVCodec h264_decoder = {
"h264", //name
CODEC_TYPE_VIDEO, //type
CODEC_ID_H264, //id
sizeof(H264Context), //private data size
decode_init, // (*init)()
NULL, // (*encode)()
decode_end, // (*close)()
decode_frame, // (*decode)()
/*CODEC_CAP_DRAW_HORIZ_BAND |*/ CODEC_CAP_DR1 | CODEC_CAP_TRUNCATED | CODEC_CAP_DELAY, //capability
NULL, //next
flush_dpb, // (*flush)()
};

5.12.2010

Try VLC for WINCE

雖然網站說 因為沒有 developer 對這個有興趣,所以ce 的 port 中止了,
但是還有是 for CE 的 nightly build 可以 download (不知道是不是因為 auto build 的關係)

http://nightlies.videolan.org/build/wince/?C=M;O=D

說明是 for XScale,還是 dowload 下來試試。

使用KITL 來 run ,結果有 message ; find no plugin, check --plugin-path
所以改用 target control 來 run , command:
>s SDMMC\vlc\vlc --plugin-path SDMMC\vlc\plugins
就可以 load plugins 的 dll 了。
結果 load lib555 出現 exception
PB Debugger Loaded 'liblive555_plugin.dll', no matching symbolic information found.
Exception 'Data Abort' (4): Thread-Id=059c003e(pth=8e23815c), Proc-Id=059b002a(pprc=8ddbc36c) 'vlc.exe',
VM-active=059b002a(pprc=8ddbc36c) 'vlc.exe'
PC=42f14df8(liblive555_plugin.dll+0x00084df8) RA=42f0a1a8(liblive555_plugin.dll+0x0007a1a8) SP=0011f848,
BVA=6d9dc460

Data Abort: 0xC0000005 0x42F14DF8
把lib555 拿掉試試看..
變成
PB Debugger Loaded 'libyuy2_i422_plugin.dll', no matching symbolic information found.
PB Debugger Loaded 'libzip_plugin.dll', no matching symbolic information found.
Exception 'Raised Exception' (-1): Thread-Id=045f002e(pth=8e23815c), Proc-Id=05fc002a(pprc=8ddbc36c) 'vlc.exe',
VM-active=05fc002a(pprc=8ddbc36c) 'vlc.exe'
PC=4002c508(coredll.dll+0x0001c508) RA=93b3c4c8(kernel.dll+0x000064c8) SP=0011f648, BVA=ffffffff
一樣,rename掉,繼續..
奇怪,fail在
B Debugger Loaded 'libyuy2_i422_plugin.dll', no matching symbolic information found.
Exception 'Raised Exception' (-1): Thread-Id=0591003a(pth=8e23815c), Proc-Id=058a005a(pprc=8e946000) 'vlc.exe', VM-active=058a005a(pprc=8e946000) 'vlc.exe'
PC=4002c508(coredll.dll+0x0001c508) RA=93b3c4c8(kernel.dll+0x000064c8) SP=0011f648, BVA=ffffffff
猜是 memory 不夠的問題,刪掉一堆dll看看.

5.10.2010

ceappcompat.exe -- check if app runnable on CE 6.0

原來 CE 6.0 有附一個工具 ceappcompat.exe ,可以用來檢查 5.0 的 ap 在 6.0 能不能 run.

在:
http://blogs.msdn.com/mikehall/archive/2006/11/04/ceappcompat-location.aspx

C:\WINCE600\PUBLIC\COMMON\OAK\BIN\I386

tcpmp for WINCE 6.0

tcpmp 啟動時會自動 load 目錄下所有的 *.plg 檔。
其實這些檔就是 DLL,但是為了防止load 到騎他program 的 DLL,所以rename 成 plg。

tcpmp 的 VS2005 project 是一個基本的 project,只包涵 network, mpeg1.. etc 等少數的 codec 在裡面,這些 codec project 會 build 成 *.plg 檔,放在 release/output folder 中。

其他 project (ffmpeg, aac, ac3..) 要 build 的話,可以直接開啟該 codec project 的 vcp (for eVC 得的那個,不是VC6的),然後修改一下:
  • 要 把 common.lib 加入 -- 要把 common.lib 所在path 加到 linker 的 Additional Library Direction 中。把 common.lib 加到 Linker - Input - Additional Dependency 中
  • 修改 Linker - Output File : $(OutDir)\$(ProjectName).plg
  • 修改 Linker - Debugging - Generate Program Database : $(OutDir)/ffmpeg.pdb
這樣就可以了。
也可以直接把project 加到 tcpmp 中。

tcpmp 有使用 類似 llvm 的技巧,在 port.h 中定義 CONFIG_DYNCODE。
undefine 的話就可以不用 (但是code 沒有很完全)。

tcpmp 啟動會 load *.txt ,這是原來在 language folder 下的 界面字串定義檔,所以也要 copy 到 執行目錄下。





diff --git a/common/blit/blit_arm_fix.c b/common/blit/blit_arm_fix.c
index 7af2299..6131e95 100644
--- a/common/blit/blit_arm_fix.c
+++ b/common/blit/blit_arm_fix.c
@@ -25,7 +25,7 @@
#include "../dyncode/dyncode.h"
#include "blit_soft.h"

-#if defined(ARM)
+#if defined(ARM) && defined(CONFIG_DYNCODE)

typedef struct stack
{
diff --git a/common/blit/blit_arm_gray.c b/common/blit/blit_arm_gray.c
index 9b7797b..8d8b636 100644
--- a/common/blit/blit_arm_gray.c
+++ b/common/blit/blit_arm_gray.c
@@ -25,7 +25,7 @@
#include "../dyncode/dyncode.h"
#include "blit_soft.h"

-#if defined(ARM)
+#if defined(ARM) && defined(CONFIG_DYNCODE)

typedef struct stack
{
diff --git a/common/blit/blit_arm_half.c b/common/blit/blit_arm_half.c
index a59d66c..089109d 100644
--- a/common/blit/blit_arm_half.c
+++ b/common/blit/blit_arm_half.c
@@ -29,7 +29,7 @@
// DstAlignPos 4
// SrcAlignPos 2

-#if defined(ARM)
+#if defined(ARM) && defined(CONFIG_DYNCODE)

typedef struct stack
{
diff --git a/common/blit/blit_arm_packed_yuv.c b/common/blit/blit_arm_packed_yuv.c
index 494721b..871bd36 100644
--- a/common/blit/blit_arm_packed_yuv.c
+++ b/common/blit/blit_arm_packed_yuv.c
@@ -25,7 +25,7 @@
#include "../dyncode/dyncode.h"
#include "blit_soft.h"

-#if defined(ARM)
+#if defined(ARM) && defined(CONFIG_DYNCODE)

typedef struct stack
{
diff --git a/common/blit/blit_arm_rgb16.c b/common/blit/blit_arm_rgb16.c
index 3c153f1..6de28fb 100644
--- a/common/blit/blit_arm_rgb16.c
+++ b/common/blit/blit_arm_rgb16.c
@@ -25,7 +25,7 @@
#include "../dyncode/dyncode.h"
#include "blit_soft.h"

-#if defined(ARM)
+#if defined(ARM) && defined(CONFIG_DYNCODE)

// R0..R6 temporary
// R7 Pos(when Stretch)
diff --git a/common/blit/blit_arm_stretch.c b/common/blit/blit_arm_stretch.c
index 5855d9c..be36ed8 100644
--- a/common/blit/blit_arm_stretch.c
+++ b/common/blit/blit_arm_stretch.c
@@ -27,7 +27,7 @@
#include "../dyncode/dyncode.h"
#include "blit_soft.h"

-#if defined(ARM)
+#if defined(ARM) && defined(CONFIG_DYNCODE)

typedef struct stack
{
diff --git a/common/blit/blit_arm_yuv.c b/common/blit/blit_arm_yuv.c
index 3e4d612..38ae8cd 100644
--- a/common/blit/blit_arm_yuv.c
+++ b/common/blit/blit_arm_yuv.c
@@ -29,7 +29,7 @@
// DstAlignPos 8
// SrcAlignPos 8

-#if defined(ARM)
+#if defined(ARM) && defined(CONFIG_DYNCODE)

// R0..R4 temporary
// R5 DiffMask (when Diff)
diff --git a/common/blit/blit_soft.c b/common/blit/blit_soft.c
index 3b8770c..f4cb08f 100644
--- a/common/blit/blit_soft.c
+++ b/common/blit/blit_soft.c
@@ -1795,7 +1795,7 @@ static bool_t BlitCompile(blit_soft* p,
p->SrcPos[i] = BitMaskPos(p->Src.BitMask[i]);
}

-#if defined(ARM)
+#if defined(ARM) && defined(CONFIG_DYNCODE)
if ((p->Dst.Flags & PF_RGB) && p->Dst.BitCount==16 && !p->SrcYUV)
Any_RGB_RGB(p);
if (!p->SrcYUV && DstPlanarYUV && p->RScaleX==16 && p->RScaleY==16)
@@ -1848,11 +1848,14 @@ static bool_t BlitCompile(blit_soft* p,
}
#endif

+#if defined(ARM) && defined(CONFIG_DYNCODE)
+
CodeBuild(&p->Code);
if (p->Code.Size)
p->Entry = (blitsoftentry)p->Code.Code;
else
p->Entry = NULL;
+#endif

#if defined(_M_IX86) && !defined(TARGET_SYMBIAN)

diff --git a/common/blit/blit_wmmx_fix.c b/common/blit/blit_wmmx_fix.c
index 6ffb3bb..1b3ec38 100644
--- a/common/blit/blit_wmmx_fix.c
+++ b/common/blit/blit_wmmx_fix.c
@@ -32,7 +32,7 @@
// RScale==32 && !SwapXY 16x4 -> 8x2
// RScale==32 && SwapXY 4x16 -> 8x2

-#if defined(ARM)
+#if defined(ARM) && defined(CONFIG_DYNCODE)

typedef struct stack
{
diff --git a/common/cpu/cpu.c b/common/cpu/cpu.c
index a68bad6..6e410d1 100644
--- a/common/cpu/cpu.c
+++ b/common/cpu/cpu.c
@@ -64,8 +64,14 @@ void CPUDetect(cpudetect* p)

#ifdef ARM
p->Arch = T("ARM");
- SafeGetCpuId(0,CpuId);
+ //SafeGetCpuId(0,CpuId);

+ p->ICache = 16*1024;
+ p->DCache = 16*1024;
+ Caps |= CAPS_ARM_GENERAL;
+ p->Model = T("1020E");
+ Caps |= CAPS_ARM_5E;
+/*
if (CpuId[0])
{
p->ICache = 512 << ((CpuId[1] >> 6) & 7);
@@ -156,7 +162,7 @@ void CPUDetect(cpudetect* p)
}
}
}
-
+*/
#elif defined(MIPS)
SafeGetCpuId(0,CpuId);
p->Arch = T("MIPS");
diff --git a/common/pcm/pcm_arm.c b/common/pcm/pcm_arm.c
index 8ebe96e..269fdc1 100644
--- a/common/pcm/pcm_arm.c
+++ b/common/pcm/pcm_arm.c
@@ -25,7 +25,7 @@
#include "../dyncode/dyncode.h"
#include "pcm_soft.h"

-#if defined(ARM)
+#if defined(ARM) && defined(CONFIG_DYNCODE)

typedef struct stack
{
diff --git a/common/portab.h b/common/portab.h
index 3b47674..bce4701 100644
--- a/common/portab.h
+++ b/common/portab.h
@@ -71,7 +71,7 @@
#endif

#if defined(ARM) || defined(MIPS) || defined(SH3)
-#define CONFIG_DYNCODE
+#undef CONFIG_DYNCODE
#endif

#if defined(_M_IX86)
diff --git a/common/win32/platform_win32.c b/common/win32/platform_win32.c
index 0df8c42..78ae591 100644
--- a/common/win32/platform_win32.c
+++ b/common/win32/platform_win32.c
@@ -670,7 +670,7 @@ void Platform_Init()

BacklightEvent = CreateEvent(NULL, FALSE, FALSE, T("TIMEOUTDISPLAYOFF"));

- Power_Init();
+ //Power_Init();
#endif

if (!Context()->CodePage)
@@ -976,6 +976,7 @@ bool_t GetDisplayPower()

int SetDisplayPower(bool_t State,bool_t Force)
{
+/*
VIDEO_POWER_MANAGEMENT VPM;
HDC DC;

@@ -991,6 +992,7 @@ int SetDisplayPower(bool_t State,bool_t Force)
ExtEscape(DC, SETPOWERMANAGEMENT, sizeof(VPM), (LPCSTR) &VPM, 0, NULL);
ReleaseDC(NULL, DC);
}
+*/
return ERR_NONE;
}

diff --git a/common/win32/str_win32.c b/common/win32/str_win32.c
index 77bc3bf..04f8a34 100644
--- a/common/win32/str_win32.c
+++ b/common/win32/str_win32.c
@@ -24,6 +24,7 @@
#include "../common.h"
#include "../gzip.h"

+
#define MAXTEXT 20000

#if defined(TARGET_WIN32) || defined(TARGET_WINCE)
diff --git a/interface/win32/interface.c b/interface/win32/interface.c
index ac68894..375060f 100644
--- a/interface/win32/interface.c
+++ b/interface/win32/interface.c
@@ -2765,10 +2765,12 @@ static bool_t Proc(intface* p, int Msg, uint32_t wParam, uint32_t lParam, int* R
break;

case WM_KEYUP:
+/*
#if defined(TARGET_WINCE)
if (!GetDisplayPower())
SetDisplayPower(0,1);
#endif
+*/
Key = WinKeyState(KeyRotate(p,wParam));
for (i=0;i<HOTKEYCOUNT;++i)
if (p->HotKey[i] && (p->HotKey[i] & ~HOTKEY_KEEP) == Key)
@@ -2792,6 +2794,7 @@ static bool_t Proc(intface* p, int Msg, uint32_t wParam, uint32_t lParam, int* R
break;

case WM_HOTKEY:
+/*
#if defined(TARGET_WINCE)
if ((LOWORD(lParam) & MOD_KEYUP))
{
@@ -2800,7 +2803,8 @@ static bool_t Proc(intface* p, int Msg, uint32_t wParam, uint32_t lParam, int* R
}
else
#endif
- PostMessage(p->Win.Wnd,WM_COMMAND,wParam,0);
+*/
+ PostMessage(p->Win.Wnd,WM_COMMAND,wParam,0);
*Result = 0;
return 1;

diff --git a/interface/win32/win_win32.c b/interface/win32/win_win32.c
index 7c92a1f..821cbe4 100644
--- a/interface/win32/win_win32.c
+++ b/interface/win32/win_win32.c
@@ -2592,6 +2592,7 @@ static void HandleMessage(win* p,MSG* Msg)
{
int Result;

+/*
#if defined(TARGET_WINCE)
if (Msg->message == WM_HIBERNATE)
{
@@ -2599,7 +2600,7 @@ static void HandleMessage(win* p,MSG* Msg)
NodeHibernate();
}
#endif
-
+*/
if (Msg->message >= WM_APP + 0x200 &&
Msg->message <= WM_APP + 0x220)
{

5.07.2010

Log : trace tcpmp exception on CE 6.0 - assembly code

看一下 assembly code exception 的原因:
p->Entry(p,Dst,Src,*DstLength,&p->State,Volume)

對應的 assmbly code是:
41041954 E59D3044             ldr         r3, Volume, #0x44
41041958 E58D3004 str r3, [sp, #4]
4104195C E59D302C ldr r3, p, #0x2C
41041960 E2833004 add r3, r3, #4
41041964 E58D3000 str r3, [sp]
41041968 E59D3038 ldr r3, DstLength, #0x38
4104196C E5933000 ldr r3, [r3]
41041970 E59D2034 ldr r2, Src, #0x34
41041974 E59D1030 ldr r1, Dst, #0x30
41041978 E59D002C ldr r0, p, #0x2C
4104197C E59DE02C ldr lr, p, #0x2C
41041980 E59E4000 ldr r4, [lr]
41041984 E1A0E00F mov lr, pc
41041988 E1A0F004 mov pc, r4
可以看到 參數傳遞是 r0~r3,stack,stack

Entry( ) 內的 assembly code 是:
010A0000 00000000             andeq       r0, r0, r0
010A0004 00000000 andeq r0, r0, r0
010A0008 00000000 andeq r0, r0, r0
010A000C 00000000 andeq r0, r0, r0
010A0010 E59D4028 ldr r4, [sp, #0x28]
010A0014 E5947004 ldr r7, [r4, #4]
010A0018 E5948008 ldr r8, [r4, #8]
010A001C E59D002C ldr r0, [sp, #0x2C]
010A0020 E3570C01 cmp r7, #1, 24
010A0024 1A000006 bne 010A0044
010A0028 E0D920F2 ldrsh r2, [r9], #2
010A002C E0020290 mul r2, r0, r2
010A0030 E1A02442 mov r2, r2, asr #8
010A0034 E0CB20B2 strh r2, [r11], #2
010A0038 E15B000E cmp r11, lr
010A003C 1AFFFFF9 bne 010A0028
010A0040 E8BD9FF0 ldmia sp!, {r4 - r12, pc}
010A0044 E1A03428 mov r3, r8, lsr #8
010A0048 E1A03083 mov r3, r3, lsl #1
010A004C E19920F3 ldrsh r2, [r9, +r3]
010A0050 E0888007 add r8, r8, r7
010A0054 E0020290 mul r2, r0, r2
010A0058 E1A02442 mov r2, r2, asr #8
010A005C E0CB20B2 strh r2, [r11], #2
010A0060 E15B000E cmp r11, lr
010A0064 1AFFFFF6 bne 010A0044
010A0068 E8BD9FF0 ldmia sp!, {r4 - r12, pc}
看到好像argument 完全不對應,r0 甚至被破壞..
在 CompileCode( ), BuildCode( ) 完後,0x10A000 的內容:
010A0000  F0 5F 2D E9 00 B0 91 E5 03 E0 8B E0 00 90 92 E5  ._-.............
010A0010 28 40 9D E5 04 70 94 E5 08 80 94 E5 2C 00 9D E5 (@...p......,...
010A0020 01 0C 57 E3 06 00 00 1A F2 20 D9 E0 90 02 02 E0 ..W...... ......
010A0030 42 24 A0 E1 B2 20 CB E0 0E 00 5B E1 F9 FF FF 1A B$... ....[.....
010A0040 F0 9F BD E8 28 34 A0 E1 83 30 A0 E1 F3 20 99 E1 ....(4...0... ..
010A0050 07 80 88 E0 90 02 02 E0 42 24 A0 E1 B2 20 CB E0 ........B$... ..
010A0060 0E 00 5B E1 F6 FF FF 1A F0 9F BD E8 00 00 00 00 ..[.............

可以看到是不一樣的,0x10A0000 到 0x10A000F 都被清成 0x00。

之後的 code 果然跟 cache 有關,code都做完後,就要:
void CodeUnlock(void* Code,int Size)
{
DWORD Protect;
VirtualProtect(Code,Size,PAGE_EXECUTE_READ,&Protect);

if (FuncCacheSync)
FuncCacheSync(CACHE_SYNC_INSTRUCTIONS);

}
很奇怪的是,如果在這其中 break 一下(用KITL),或是完成後,用 DebugWindow 的 Memory 來 check 一下,資料就會正確。
真的是cache 的關係? 這個 ARM11 有L2 cache..

msdn 這一頁 有列出,CacheSync 是 Kernel Mode Only.

5.06.2010

Log : tcpmp - add ffmpeg codec support

將 ffmpeg project 加入 tcpmp 中。
因為這樣加入的不會有 tcpmp 的 project setting,所以要手動加入 common.lib 的 library path。
還要手動把 common.lib 加入 link library
還要手動設定 output path 到 tcpmp\... 下 (跟大家一樣)

tcpmp 啟動會自動 load 所有 *.plg 檔,所以 ffmpeg.plg 會被 load 進去。

開啟 MPEG2 file (DVD) 試試... 一樣發生 exception.. 好像跟 pcm 一樣,是在 assembly - entry( ) 的地方

Log : trace tcpmp exception on CE 6.0 - remove exception

只好啟動 KITL,作 單布執行。
  • enable debug ouput ,rebuild all。
  • copy all the files in release folder (其實重要的是 pdb) to WINCE600 project release folder
  • copy language filt (*.txt) to release WINCE600 project release folder
  • 啟動 KITL 連線,設定 player.exe, common.dll, interface.plg 都由 release folder 取得
  • 啟動 target control,s player.exe
  • 選一個 mp3 file
  • exception 發生!!
  • 從 call stack 看exception 位置
查到是 common\pcm\pcm_soft.c 的 PCMConvert( ),在執行
p->Entry(p,Dst,Src,*DstLength,&p->State,Volume);

中 發生 exception。

trace code,看到 pcm_arm.c
這個好像是在作 arm assembler.....
            CodeStart(&p->Code);
PCMCompile(p);
CodeBuild(&p->Code);
if (p->Code.Size)
p->Entry = (pcmsoftentry)p->Code.Code;
其中 PCMCompile()..
    // dst pointers
I2C(LDR,R11,R1,0);
I3(ADD,R14,R11,R3); // dstend

// src pointers
I2C(LDR,R9,R2,0);

I2C(LDR,R4,SP,OFS(stack,State));
I2C(LDR,R7,R4,OFS(pcmstate,Step));
I2C(LDR,R8,R4,OFS(pcmstate,Pos));

不知道這一段 assmbly code 為什麼不能在 CE 6.0 (或是因為 arm11 的關係?)。
在播放wav時,entry( ) 的內容是:
10A0000 00000000             andeq       r0, r0, r0
010A0004 00000000 andeq r0, r0, r0
010A0008 00000000 andeq r0, r0, r0
010A000C 00000000 andeq r0, r0, r0
010A0010 E59D4028 ldr r4, [sp, #0x28]
010A0014 E5947004 ldr r7, [r4, #4]
010A0018 E5948008 ldr r8, [r4, #8]
010A001C E59D002C ldr r0, [sp, #0x2C]
010A0020 E3570C01 cmp r7, #1, 24
010A0024 1A000006 bne 010A0044
010A0028 E0D920F2 ldrsh r2, [r9], #2
010A002C E0020290 mul r2, r0, r2
010A0030 E1A02442 mov r2, r2, asr #8
010A0034 E0CB20B2 strh r2, [r11], #2
010A0038 E15B000E cmp r11, lr
010A003C 1AFFFFF9 bne 010A0028
010A0040 E8BD9FF0 ldmia sp!, {r4 - r12, pc}
010A0044 E1A03428 mov r3, r8, lsr #8
010A0048 E1A03083 mov r3, r3, lsl #1
010A004C E19920F3 ldrsh r2, [r9, +r3]
010A0050 E0888007 add r8, r8, r7
010A0054 E0020290 mul r2, r0, r2
010A0058 E1A02442 mov r2, r2, asr #8
010A005C E0CB20B2 strh r2, [r11], #2
010A0060 E15B000E cmp r11, lr
010A0064 1AFFFFF6 bne 010A0044
010A0068 E8BD9FF0 ldmia sp!, {r4 - r12, pc}

所以這一段不用 assembly code 改用 UniversalType( )
#if 0 //(defined(ARM) || defined(SH3) || defined(MIPS)) && defined(CONFIG_DYNCODE)
CodeStart(&p->Code);
PCMCompile(p);
CodeBuild(&p->Code);
if (p->Code.Size)
p->Entry = (pcmsoftentry)p->Code.Code;
#else
p->SrcType = UniversalType(&p->Src);
p->DstType = UniversalType(&p->Dst);
p->Entry = PCMUniversal;
還要記得把 UniversalType( ) 的 define guard um-comment 掉。

!!這樣就可沒有 exception 了。

tcpmp source code - use default language, exception

tcpmp 會利用 GetLocaleInfo( ) 來取得 platform 的語系資訊。

如過想要強制使用英文,可以修改 platform_win32.c 的 DefaultLang( ),強制 return LANG_DEFAULT 就可以。

tcpmp 還有一段 : SafeException( ) ,將整個 code body 包起來:
#define SAFE_BEGIN __try {
.... code body...
#define SAFE_END ;} __except (SafeException(_exception_info())) {}
SafeException( ) 的 funciton 是:
int SafeException(void* p)
{
EXCEPTION_POINTERS* Data = (EXCEPTION_POINTERS*)p;
stream* File;
tchar_t Path[MAXPATH];

if (Context())
{
GetDebugPath(Path,TSIZEOF(Path),T("crash.txt"));
File = StreamOpen(Path,1);
if (File)
{
void** Stack;
contextreg* r;
int No;
const uint8_t* ContextRecord = (const uint8_t*) Data->ContextRecord;
EXCEPTION_RECORD* Record = Data->ExceptionRecord;
tchar_t* Name;
int DllBase;
tchar_t DllName[MAXPATH];

StreamPrintf(File,T("%s %s crash report\n----------------------------\n"),
Context()->ProgramName,Context()->ProgramVersion);

switch (Record->ExceptionCode)
{
case STATUS_ACCESS_VIOLATION: Name = T("Access violation"); break;
case STATUS_BREAKPOINT: Name = T("Breakpoint"); break;
case STATUS_DATATYPE_MISALIGNMENT: Name = T("Datatype misalignment"); break;
case STATUS_ILLEGAL_INSTRUCTION: Name = T("Illegal instruction"); break;
case STATUS_INTEGER_DIVIDE_BY_ZERO: Name = T("Int divide by zero"); break;
case STATUS_INTEGER_OVERFLOW: Name = T("Int overflow"); break;
case STATUS_PRIVILEGED_INSTRUCTION: Name = T("Priv instruction"); break;
case STATUS_STACK_OVERFLOW: Name = T("Stack overflow"); break;
default: Name = T("Unknown"); break;
}

if (!NodeLocatePtr(Record->ExceptionAddress,DllName,TSIZEOF(DllName),&DllBase))
{
DllName[0] = 0;
DllBase = (int)Record->ExceptionAddress;
}
StreamPrintf(File,T("%s(%08x) at %08x (%s:%08x)"),Name,Record->ExceptionCode,Record->ExceptionAddress,DllName,DllBase);

if (Record->ExceptionCode == STATUS_ACCESS_VIOLATION)
{
if (Record->ExceptionInformation[0])
Name = T("Write to");
else
Name = T("Read from");
StreamPrintf(File,T("\n%s %08x"),Name,Record->ExceptionInformation[1]);
if (NodeLocatePtr((void*)Record->ExceptionInformation[1],DllName,TSIZEOF(DllName),&DllBase))
StreamPrintf(File,T(" (%s:%08x)"),DllName,DllBase);
}

// context

StreamPrintf(File,T("\n\ncpu dump:"));

for (r=Reg;r->Name;++r)
{
void* Ptr = *(void**)(ContextRecord+r->Ofs);

StreamPrintf(File,T("\n%-5s = %08x"),r->Name,Ptr);

if (NodeLocatePtr(Ptr,DllName,TSIZEOF(DllName),&DllBase))
StreamPrintf(File,T(" (%s:%08x)"),DllName,DllBase);
}

if (r->Ofs >= 0)
{
StreamPrintf(File,T("\n\nstack dump:"));

Stack = *(void***)(ContextRecord+r->Ofs);
for (No=0;No<256;++No,++Stack)
{
if (!IsBadReadPtr(Stack,sizeof(void*)))
{
void* Ptr = *Stack;

StreamPrintf(File,T("\n%08x %08x"),Stack,Ptr);

if (NodeLocatePtr(Ptr,DllName,TSIZEOF(DllName),&DllBase))
StreamPrintf(File,T(" (%s:%08x)"),DllName,DllBase);
}
else
StreamPrintf(File,T("\n%08x ????????"),Stack);
}
}

StreamPrintf(File,T("\n\n"));
TRY_BEGIN
{
NodeBroadcast(NODE_CRASH,NULL,0);
}
TRY_END
NodeDump(File);
StreamClose(File);
}

MessageBox(NULL,LangStr(PLATFORM_ID,PLATFORM_CRASH_MESSAGE),
LangStr(PLATFORM_ID,PLATFORM_CRASH_TITLE),MB_OK|MB_SETFOREGROUND|MB_TOPMOST|MB_ICONSTOP);
}

Mem_Done(); // use safevirtualfree (if done by OS, it could be buggy on O2 Atom)
TerminateProcess(GetCurrentProcess(),1);
return 1;
}
就是把 exception 的資料存入 crash.txt。


有關 ce 的 try except,在這一篇有說明 http://windows-ce-dox.net/MS.Press-Programming.Microsoft/html/ch087.htm

Log : trace tcpmp exception on CE 6.0 SetKMode

在 CE 6.0 不能 run 的原因好像是 SetKMode( ) 嗎?

雖然 SetKMode 在 CE 6.0 已經不支援了,但是 CoreDll.dll 裡還是找得到 function。

SetKMode( ) 在 DetectCPU() 中使用,主要在知道 cpu type,設定好一堆 instruction capabilty。
這樣之後的一堆 image process 會依照 cpu 的 capability (指令集) 決定 code。

只好 hard code
if ((CpuId[0] & 0xFF000000) == 0x41000000) //arm
  {   Caps |= CAPS_ARM_GENERAL;
   switch ((CpuId[0] >> 4) & 0xFFF)
   {
   case 0x920: p->Model = T("920T");
 break;   case 0x922: p->Model = T("922T"); break;   case 0x926: p->Model = T("926E"); Caps |= CAPS_ARM_5E; break;   case 0x940: p->Model = T("940T"); break;   case 0x946: p->Model = T("946E"); Caps |= CAPS_ARM_5E; break;   case 0xA22: p->Model = T("1020E"); Caps |= CAPS_ARM_5E; break;   }  }
還有
if (CpuId[0])  {   p->ICache = 512 << ((CpuId[1] >> 6) & 7);   p->DCache = 512 << ((CpuId[1] >> 18) & 7);  }
所以直接寫
p->ICache = 16*1024;  p->DCache = 16*1024;  Caps |= CAPS_ARM_GENERAL;  p->Model = T("1020E");  Caps |= CAPS_ARM_5E;
其他的 code 都不要了。
這樣 exception 就沒了。





Log : Build tcpmp - part II . exception on SetKMode

在 CE 6.0 不能 run 的原因好像是 SetKMode( ) 嗎?

雖然 SetKMode 在 CE 6.0 已經不支援了,但是 CoreDll.dll 裡還是找得到 function。

SetKMode( ) 在 DetectCPU() 中使用,主要在知道 cpu type,設定好一堆 instruction capabilty。
這樣之後的一堆 image process 會依照 cpu 的 capability (指令集) 決定 code。

只好 hard code
if ((CpuId[0] & 0xFF000000) == 0x41000000) //arm
  {   Caps |= CAPS_ARM_GENERAL;
   switch ((CpuId[0] >> 4) & 0xFFF)
   {
   case 0x920: p->Model = T("920T"); break;
   case 0x922: p->Model = T("922T"); break;
   case 0x926: p->Model = T("926E"); Caps |= CAPS_ARM_5E; break;
   case 0x940: p->Model = T("940T"); break;
   case 0x946: p->Model = T("946E"); Caps |= CAPS_ARM_5E; break;
   case 0xA22: p->Model = T("1020E"); Caps |= CAPS_ARM_5E; break;
   }  }
還有
if (CpuId[0])
  {   p->ICache = 512 << ((CpuId[1] >> 6) & 7);
   p->DCache = 512 << ((CpuId[1] >> 18) & 7);
  }
所以直接寫
p->ICache = 16*1024;
  p->DCache = 16*1024;
  Caps |= CAPS_ARM_GENERAL;
  p->Model = T("1020E");
  Caps |= CAPS_ARM_5E;
其他的 code 都不要了。
這樣 exception 就沒了。



... 然後第二個 exception 出現....

5.05.2010

Log : Build tcpmp

tcpmp 0.72RC1 source code 的 download 位置是: http://picard.exceed.hu/tcpmp/test/
tcpmp 的開發已經終止,原公司開發 betaplayer,即將 opensource (?)
開啟 tcpmp.sln 看,是 visual studio V9.0 ,所以使用 VS2005 開啟。

build - 選 pocket pc 2003 (ARMV4) 版本build OK。
===> 但是 在 CE 5.0 上不能run,連 launch 都不行
發現是 load Interface.plg fail -- 根本沒有這個檔。
follow readme,用 eVC d開啟,雖然 build fail,但是有出現 interface.plg 在 output folder。

查看發現, interface 這個 subproject 有設定 output name 是 interface.plg (所以應該是 Dll rename 的)

看VS2005 的 interface subproject,也有設定 output name 是 plg。
==>在 "packet pc 2003 (armv4)\debug" 下有 interface.plg
copy 到 SD card 上後,player.exe 果然 run 起來,接著出現 exception.. 畫面顯示: Language files (*.txt, *.tgz) ar missing.
搜尋*.txt,在 lang 這個 folder 裡有很多語系的 txt file,都 copy 到 SD 上, run player .exe 就沒有 error message 了。

在 CE 5.0 上 run OK,還可以開 WAVE file 播放,但是在 CE 6.0 就會有問題。
==>有 exception :
Exception 'Undefined Instruction' (1): Thread-Id=058e007a(pth=8db9e7c8), Proc-Id=0563008a(pprc=8dd91000) 'PLAYER.EXE', VM-active=0563008a(pprc=8dd91000) 'PLAYER.EXE'
PC=41067188(common.dll+0x00097188) RA=41067498(common.dll+0x00097498) SP=0003fa8c, BVA=00000000

查到好像是在 Platform_Init ( ) 裡的 NodeRegisterClass(&Platform) 裡。
但是同一function : NodeRegisterClass

死在 CallCreate( ) ,這是一個 recursive 程式。