Archive for 二月, 2010

State Monad笔记一

Posted on 二月 25th, 2010 in 笔记 | 2 Comments »

去年听王猫猫讲过之后就一直心安理得再也没看过Monad,以至于到昨天还不知State Monad为何物…囧

关于State Monad,似乎见过的教材都是拿随机数作例子,拿Monad跟一堆let对比,满眼的二元组绕啊绕啊,我给你个Monad让它不那么绕(存疑)~其实什么东西理解不了,往往就是因为不知道它怎么用(比如复变函数 =”=)

换个例子,像那个筛掉一个List中重复元素的nub函数多好….在命令式语言下边大约是这样:

def nub(xs)
    result=[]
    for x in xs
        if not result.include? x
	     result << x
    return result

若在函数式语言下边,用迭代大约是这样:

inub :: (Eq a) => [a] -> [a]
inub = inub' [] 
inub' rs [] = rs
inub' rs (x:xs) 
    | x `elem` rs    = inub' rs xs
    | otherwise     = inub' (rs++[x]) xs

inub’这个函数中有个rs参数,就相当于前面那个result变量。它的值会随着迭代发生改变,不同之处就是ts是引用透明的没有副作用。每次函数调用就是一个状态,状态就是一个值。

不喜欢给函数多加个参数怎么办?State Monad可以做到。在State Monad里面有两个函数可以使用:put,设置当前状态的值;get,获得当前状态的值。就相当于给函数加了一个(只有一个)可变的全局变量,在调用别的函数或者递归时这个值可以保留。

State Monad版本(我没觉得好看 =”=):

mnub :: (Eq a) => [a] -> [a]
mnub xs = evalState (mnub' xs) []
mnub' [] = get
mnub' (x:xs) = do {
    rs <- get;
    if x `elem` rs then (do {
        mnub' xs;
    })
    else (do {
        put $ (rs++[x]);
        mnub' xs;
    });
}

原理大约就是do-notation中的每个语句都是用一个wrapper的类型包起来再不断往下传递么,而State的wrapper就是个类型为(a,s)的二元组(外加一层lambda?),每个语句的返回值放到s里,那个全局的值则放在a里…具体还有点迷糊,哪天推导下再说好了 >_<

简介AT&T风格汇编

Posted on 二月 17th, 2010 in 翻译 | 14 Comments »

作者:vivek
翻译:ssword
原文:http://sig9.com/articles/att-syntax

本文粗谈一下gas(1)的汇编语法,即AT&T风格汇编。初次接触很可能会觉得它别扭,不过若有其他汇编语言的基础,稍事了解即可快速上手。我假设你熟悉INTEL风格汇编——也就是INTEL手册中的那种风格。方便起见,我就用NASM(Netwide Assembler)来做比对。

gas属于GNU Binary Utilities(binutils),也是GCC的一个后端。对编写较长的汇编程序而言它并非首选,不过对于类Unix系统的内核级hacking,它就无可替代了。选择AT&T风格使得gas饱受争议,人们总说它只是GCC的后端,而对开发者不友好。INTEL风格汇编的教众也认为,它在可读性及编译上几乎是令人窒息。尽管如此,有一点必须了解:很多操作系统都选择了gas作为底层代码的汇编器。

基本形式

AT&T汇编程序的结构与其他汇编大同小异,伪指令、标签、指令—即最多带三个操作数的助记符。要说AT&T汇编的不同,最显眼的地方就是它操作数的顺序。

例如,一个简单的数据移动指令在INTEL风格下边是这个样子:

mnemonic	destination, source

在AT&T风格下边则是这样:

mnemonic	source, destination

一部分人(包括我)觉得这种格式更贴切。接下来说说AT&T风格指令中的操作数。

寄存器

每个IA-32架构寄存器的名字必须以’%'作前缀,如%al,%bx,%ds,%cr0,等等。

mov	%ax, %bx

如上的mov指令就是把一个16位寄存器ax中的值移动到另一个16位寄存器bx中。

字面量

每个字面量必须以’$'为前缀。 例如:

mov	$100,	%bx
mov	$A,	%al

第一个指令是把值100移动到寄存器bx中,第二个指令是把一个字节A移动到AL寄存器中。下面这个指令就是错误的:

mov	%bx,	$100

怎么说呢,这条指令是要把寄存器bx的值移动给一个字面量,显然不靠谱。

内存寻址

在AT&T风格中,内存引用起来是这个格式:

segment-override:signed-offset(base,index,scale)

按你寻址的需求,其中的部分可以省略

%es:100(%eax,%ebx,2)

注意下,基地址及偏移中的数不带前缀’$'。拿几个例子和对应的NASM风格做个比较应该好些:

GAS memory operand			NASM memory operand
------------------			-------------------
 
100					[100]
%es:100					[es:100]
(%eax)					[eax]
(%eax,%ebx)				[eax+ebx]
(%ecx,%ebx,2)				[ecx+ebx*2]
(,%ebx,2)				[ebx*2]
-10(%eax)				[eax-10]
%ds:-10(%ebp)				[ds:ebp-10]

实例:

mov	%ax,	100
mov	%eax,	-100(%eax)

第一个指令是把寄存器AX中的值移动到数据段寄存器(默认)偏移100的内存位置,第二个指令是把寄存器eax中的值移动到[eax-100]。

操作数大小

有时指明操作数的大小是必须的,尤其是移动字面量到内存。例如这个指令:

mov	$10,	100

这里只说了把值10移动到内存偏址100处,而没有说值的大小。在NASM中,这通过给操作数后面跟个byte/word/dword之类的关键词来指明。而在AT&T风格里,是通过指令中b/w/l之类的后缀指明。如:

movb	$10,	%es:(%eax)

把值为10的一个字节移动到内存地址[ex:eax],另如:

movl	$10,	%es:(%eax)

把值为10的一个长整数移动到同一位置。

再几个例子:

movl	$100, %ebx
pushl	%eax
popw	%ax

控制流程

jmp,call,ret等指令可以转移程序的执行位置。在同一代码段中跳转,是近距跳转(near)。若是跳转到不同的代码段,就是远程跳转(far)。可用的跳转地址可以来自相对偏移(label)、寄存器、内存以及段偏移指针。相对偏移通过label指明,如下:

label1:
	.
	.
  jmp	label1

使用寄存器或者内存的值做地址的操作数必须加个前缀’*'。若是远程跳转,必须加个’l'作前缀,如‘ljmp’,‘lcall’等等。例如:

GAS syntax			NASM syntax
==========			===========
 
jmp	*100			jmp  near [100]
call	*100			call near [100]
jmp	*%eax			jmp  near eax
jmp	*%ecx			call near ecx
jmp	*(%eax)			jmp  near [eax]
call	*(%ebx)			call near [ebx]
ljmp	*100			jmp  far  [100]
lcall	*100			call far  [100]
ljmp	*(%eax)			jmp  far  [eax]
lcall	*(%ebx)			call far  [ebx]
ret				retn
lret				retf
lret $0x100			retf 0x100

段偏移指针按下面的格式指明:

jmp	$segment, $offset

例如:

jmp	$0x10, $0x100000

记住这些很快就能上手了。要了解gas的更多细节,不妨参阅这个文档

遇某熊点名

Posted on 二月 10th, 2010 in 杂碎 | 7 Comments »

来自某宅熊 ≡ω≡ :http://blog.icybear.net/2010/02/desktop-ah-was-named-the.html

1、今日桌面

2、OS为?

xp

3、这张桌布是什么?你从哪里取得的?

风景?好象是flickr。

4、更换桌布的频率高吗?

还好?一个月左右吧

5、桌面有几个ICON?

囧,数数先…

6.一堆档案和捷径放得乱七八糟的桌面,你看得下去吗?

唔,习惯就好(还是懒?)

7. 有没有什么坚持点?

乱就可以了≡ω≡

8.有为了填这份接力, 还特地整理一下吗?

没有

9. 请再传给8个[我想看看他/她的桌面?]的人。

不害人啦~ ≡ω≡

Fleurer’s Stack VM

Posted on 二月 3rd, 2010 in 备忘 | 14 Comments »

http://github.com/Fleurer/fsvm
尝试用C写的堆栈机,好像烂尾了 – -!

只是个运行时,无视了语法分析。随便写的东西也没什么规划,编写的时候就郁闷不知道哪部分该归分析器那部分该归vm,也不知道现有的部分能不能真用到解释器上,于是华丽地烂尾~只实现了20来条指令,可以递归可以闭包,不过不能算乘法…囧,很简陋啦~

好像是第一次写C,不会make就先凑合了rake – -! 对C不熟悉,满地的Segmentation Fault对我们只会用printf调试的菜鸟太残酷了…用了那个保守式的gc库Boehm GC,面对满地的malloc而无free不清楚泄漏起来会怎样…囧

本来是对C++那套OO无爱,想单用struct和函数也可以OO么。于是用了C,然后就后悔了:我不想重新实现hashmap之类的东西,C++那stl多好…T_T。倒也找到了个C的泛型库khash,不过宏终究不如模板来的好看…现在想来,信息学奥赛acm中用C的那些同学做题的时候都是自己实现一遍各个数据结构么?

拿段伪代码:

def main:
     sum(10)
 
def sum(i):
     if (i==0) : return 0;
     else: return(i+sum(i-1));

放到fsvm下大约是这样:

int test_rec(){
    Op op_main[]={
        OP_PUSH_NUM, 10, 
        OP_PUSH_CONST, 0, //"sum"  
        OP_PUSH_VAR, 
        OP_CALL, 1,
        OP_RET
    };
    Op op_sum[]={
        OP_PUSH_CONST, 0, //"i"
        OP_PUSH_VAR, 
        OP_POP_TMP, 0, //store i
        OP_PUSH_TMP, 0, //push i
        OP_PUSH_NUM, 0, 
        OP_EQ,
        OP_NOT, // i!=0?
        OP_BRANCH, 3, 
            OP_PUSH_NUM, 0,
            OP_RET,
        //else
            OP_PUSH_TMP, 0, 
            OP_PUSH_NUM, 1, //1
            OP_SUB, 
            OP_PUSH_CONST, 1, //"sum"
            OP_PUSH_VAR, 
            OP_CALL, 1,   //sum(tmp[0]-1)
            OP_PUSH_TMP, 0, 
            OP_PRINT_STACK, 
            OP_ADD, //tmp[0]+sum(tmp[0]-1)
        OP_RET
    };
    Env *env=fnew_env(NULL); 
 
    Proto *p_main = fnew_proto(op_main, 0);
    fset_const(p_main, 0, fstr("sum"));
    Func* f_main=fnew_func(p_main, env);
    Obj o_main=ffunc(f_main);
 
    Proto* p_sum=fnew_proto(op_sum, 1);
    fset_const(p_sum, 0, fstr("i"));
    fset_const(p_sum, 1, fstr("sum"));
    Func* f_sum=fnew_func(p_sum, env);
    Obj o_sum=ffunc(f_sum);
 
    fbind_var(env, "sum", o_sum);
 
    fio_puts(fcall(0, f_main));
    return 0;
}

创建函数的那几个函数我自己也看着别扭…不过写C还是老实点好 >_<