注册 登录
编程论坛 汇编论坛

大家来写写汇编小玩意

Valenciax 发布于 2016-06-09 09:53, 7822 次点击
汇编一向是小众玩意,普遍认为写复杂程式挺烦,版面冷清是常态。
好吧,大的不想写,不如写写小巧东西活活脑筋吧!

出两个简单题目,看谁写得最短。

题目一.
用最短代码写出编印256个ASCII码的程式
ASCII就是美国标准信息交换代码,是基于拉丁字母的一套电脑编码系统(百度)
常见的0,1234...ABCDabcd..!@#$%^&也包含在内。
我写的一个用了7个bytes,未必是最短,等待你的写一个更短的,当然长些也不妨,
不同编码自有不同的逻辑技巧,他山之石可以攻玉。
只有本站会员才能查看附件,请 登录


题目二
用最短代码编印
0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
就是数字0..9 , 小写的a..z 和大写的A..Z
我写的一个用了26个bytes,或许还有优化空间!
只有本站会员才能查看附件,请 登录


想了一下,原来仍有缩小空间,这个用了24bytes

只有本站会员才能查看附件,请 登录


代码我会稍后贴出,大家来动动脑,贴出代码或结果,多交流才会进步,不是吗?

[此贴子已经被作者于2016-6-9 11:02编辑过]

18 回复
#2
zhulei19782016-06-09 12:17
以前都写过
#3
hu9jj2016-06-09 21:12
支持活跃气氛!
#4
Valenciax2016-06-10 08:14
楼上两位班竹有空可以玩玩。

既然暂时没人插足,先在这里贴一个正儿八经的看看,然后一步步走下去。

程序代码:
CODE segment
assume  cs:CODE,ds:CODE,es:CODE,ss:CODE
  org 100h
start:

 mov dl,'0'

 mov ah,2

 mov cx,10
s10:

 int 21h

 inc dl

 loop s10

 

 mov dl,'a'

 mov cx,26
s20:

 int 21h

 inc dl

 loop s20


 mov dl,'A'

 mov cx,26
s30:

 int 21h

 inc dl

 loop s30


 mov ah,4ch

 int 21h

CODE ENDS
END START

;39bytes

程式要小,EXE是不可行的,光PE档头就占512bytes,所以要把它变成COM格式。
COM没有档头,是直接内存的bin影像,WIN764bit以上的dos模式已经不支援运行
这些16bit程式,但仍有dosbox或模拟器可用。

COM不常见,但若要写boot起动代码,把它转换成没有档头的COM或bin是必要的。
上面这段程式编译成com,若在masm6.x
ML /AT numx.asm

若在masm5.x,步骤如下
masm numx.asm
link numx.asm
exe2bin numx.exe

好了,把编译完成,看一看,39bytes,嗯,正常写法就是这个大小。

或许有人会说,现在什么年代了,Windows都64bit,内存8G还嫌小,还在意省这么几个bytes?
我会这样回答,现在什么年代了,高铁都能跑数百里/小时,却有人喜欢踏数百公里的自行车,
苦哈哈的爬上珠穆朗玛峰,为了100公尺跑10秒内练它三五七年....

对了,就是挑战,或者游戏,写代码也可以是游戏是挑战,不当它是功课写起来起劲得多。

换一个角度看,比如我们要在一个运行中的程式插入一段代码,好不容易找到一块
50bytes的空间,你却在那里写正儿八经的标准代码,胖胖的size如下插得下那么稀缺挤逼的空间?
不想方计法把程式写得小是不行的。


回到我们这段39bytes的胖代码,如何缩小?
我们想到回圈,并把印字节改为int 10h的0Eh函数,返回dos改用int20h。

程序代码:

;31bytes
CODE segment
assume  cs:CODE,ds:CODE,es:CODE,ss:CODE
  org 100h
start:

 mov cx,10

 mov bl,2

 mov al,'0'

 mov ah,0eh
s10:

 int 10h

 inc al

 loop s10

 dec bl

 mov al,'A'

 js quit   ;-1
jz s30

 mov al,'a'
s30:

 mov cl,26

 jmp short s10
quit:

 int 20h

 
CODE ENDS
END START

这样一改,一下子缩小到31bytes,离姥姥家不远了。

#5
wmf20142016-06-10 14:20
第一个我7个字节做不到,用debug写的,得11个字节,还要向楼主学习!debug里代码如下:
0100:mov ax,0e00
0103:int 10
0105:inc al
0107:jnz 0103
0109:int 20


[此贴子已经被作者于2016-6-10 14:26编辑过]

#6
Valenciax2016-06-10 14:32
回复 5楼 wmf2014
不错,cd 20是古早dos的

[此贴子已经被作者于2016-6-10 14:57编辑过]

#7
Valenciax2016-06-10 14:39
先谢谢wmf2014班竹也来玩儿

继续我们的缩小旅程…
回圈比标准写法复杂,似乎没有再缩小的空间,此路已走到尽头。
程式的迷宫中得找另一条捷径,我们想到资料表。

先建立一个三个资料的表格,每一项资料表示:字节,长度
就这样 buff db '0',10,'a',26,'A',26
改写程式

程序代码:

CODE segment
assume  cs:CODE,ds:CODE,es:CODE,ss:CODE
  org 100h
start:

 mov si,offset buff

 mov cx,3
S10:

 push cx

 lodsw ;读入DS:SI一个word入AX,并且SI自动加1,al=字节,ah=回圈长度
mov cl,ah ;取回圈长度,这里ch已经=0
S20:

 mov ah,0eh

 int 10h

 inc al

 loop S20 ;内回圈
pop cx  ;外回圈三次
loop S10

 int 20h
buff db '0',10,'a',26,'A',26

CODE ENDS
END START


共29bytes,可以进一步缩小差距吗?
可以的,这里要借助dos的特性
特性一:
返回dos方式标准
 mov ah,4ch
 int 21h
早期的方式(不推荐)
 int 20h

另有更简单的方法:RET
程式被载内存,dos会为它保留100H作系统和程式沟通之用,
这叫PSP,PSP:0的偏移首2个BYTE是CD 20,CD 20便是INT 20H
只要CS指向PSP段,JMP 0就会跳到这道指令
而COM被载入时CS,DS,SS,ES都指向PSP段,SP指向FFFEH,栈顶处通常会是0
一通RET就弹出0值送入IP,然后跳去PSP:0,执行INT 20H

特性二:
列印字符方法有好几种,常用的
 mov dl,字符
 mov ah,2
 int 21h  ;----6 bytes

 mov al,字符
 mov ah,0eh
 int 10h  ;----也是6 bytes,但直接用al,不须经dl,在回路中应该可以减省空间
 
 其实dos另有一道后门,int 29h...快速输出字符
 mov al,字符
 int 29h

ok,把上面的程式换用这两个特性

程序代码:

CODE segment
assume  cs:CODE,ds:CODE,es:CODE,ss:CODE
  org 100h
start:

 mov si,offset buff

 mov cx,3
S10:

 push cx

 lodsw ;读入DS:SI一个word入AX,并且SI自动加1,al=字节,ah=回圈长度
mov cl,ah ;取回圈长度,这里ch已经=0
S20:

 int 29h

 inc al

 loop S20 ;内回圈
pop cx  ;外回圈三次
loop S10

 ret

buff db '0',10,'a',26,'A',26

CODE ENDS
END START


26 bytes,很接近了!

顺便说说,有了dos这两个特性,题目一的7个bytes就不是什么难事了

[此贴子已经被作者于2016-6-10 14:59编辑过]

#8
wmf20142016-06-10 15:07
回复 7楼 Valenciax
学习了。
那是不是还要利用加载com文件寄存器自动清零不给al赋初值的特性?不然我还是做不到7byte。
0100:int 29
0102:inc al
0104:jnz 100
0106:ret
#9
Valenciax2016-06-10 16:27
回复 8楼 wmf2014
对啊,根据经验,dos载入com/exe程式时,会把AX,BX和SI清0,但为了慎重起见,我特别写了一个程式,在刚进入程式时,记下并印出所有暂存器的值,分别在不同的dos版本测试,大致上是上面的观察结果
第1列是dosbox的结果
第2列是winxp下dos的结果
第3列是win7/32bit下dos的结果
都是AX,BX和SI清0,还有ch的值也都清0

只有本站会员才能查看附件,请 登录
#10
Valenciax2016-06-10 16:50
上面的显示只是仿debug的做法,这是大家习惯了的观看暂存器方式,实际上dos载入程式和debug是有分别的,dos载入大概是上面的样子,我们会发现ES指向psp段(其实ds也是指向psp,不过为了指向资料段被我改变了)
因为我是这样写的
Push ax
Mov ax,data
Mov ds,ax ;ds原值被改变了
Pop ax
Call prnitREG ;印出暂存器值



Debug载入程式则不同,除了sp,一般暂存器都清0,所以有些程式若未做暂存器初始化,在debug和dos下运行,会有不同的结果



[此贴子已经被作者于2016-6-10 17:55编辑过]

#11
wmf20142016-06-10 17:41
回复 9楼 Valenciax
细致,有钻劲!
写汇编是好久好久以前的事了,我已经忘了格式,好像什么code segement 等开头,写com程序还要加个org 100h吧!都忘了,只会用debug写些简单的,向你学习!
#12
Valenciax2016-06-10 18:39
回复 11楼 wmf2014
班竹不必客气,看过你写的C和JAVA的游戏,都挺不错,汇编在快和短占有优势,但写起大程式很费劲,还是高阶语言方便些
#13
AXRZ2016-06-11 14:07
观察了下,根据你提供的截图,CH初始为0
MOV CX,3
可以变为:
MOV CL,3
CODE segment
    assume  cs:CODE,ds:CODE,es:CODE,ss:CODE
    org 100h
start:
     mov si,offset buff
     mov cx,3    ;可改为MOV CL,3
S10:
     push cx
     lodsw ;读入DS:SI一个word入AX,并且SI自动加1,al=字节,ah=回圈长度
mov cl,ah ;取回圈长度,这里ch已经=0
S20:
     int 29h
     inc al
     loop S20 ;内回圈
    pop cx  ;外回圈三次
    loop S10
     ret

buff db '0',10,'a',26,'A',26

CODE ENDS
END START


不过在16位下也就节省了1字节
#14
Valenciax2016-06-11 16:31
回复 13楼 AXRZ
AXRZ班竹观察是对的,但24bytes版没利用这个特性,毕竟有冒险成份,万一有dos版不把ch设0,会出现错误


下面是24 bytes版
内回圈只减ah,进一步缩到24bytes,若是加上AXRZ班竹的修改
mov ch,3,那么就是23bytes !

CODE segment
assume  cs:CODE,ds:CODE,es:CODE,ss:CODE
  org 100h
start:
 mov si,offset buff
 mov cx,3
next:
 lodsw
next1:
 int 29h
 inc al
 dec ah
 jnz next1
 loop next
next2:
 ret
buff db '0',10,'a',26,'A',26
;-------------------------------------------
CODE ENDS
END START
#15
AXRZ2016-06-12 01:08
回复 14楼 Valenciax
这和你第一题的做法一个道理,如果搬到其他DOS版本下一些微小的系统调用程序时的改动都可以使这一特性失效

[此贴子已经被作者于2016-6-12 01:17编辑过]

#16
AXRZ2016-06-12 01:37
最后你出来的代码还可以省个字节:

CODE segment
assume  cs:CODE,ds:CODE,es:CODE,ss:CODE
  org 100h
start:
 mov si,offset buff
 mov cl,3
next:
 lodsw
next1:
 int 29h
 inc al    ;用inc ax,省个字节
 dec ah
 jnz next1
 loop next
next2:
 ret
buff db '0',10,'a',26,'A',26
;-------------------------------------------
CODE ENDS
END START


这样就22字节了
#17
Valenciax2016-06-12 07:04
回复 16楼 AXRZ
哈..对的,一直被直觉左右,想不起inc al和inc ax差一个bytes
#18
hu9jj2016-06-12 07:22
精益求精啊
#19
hu9jj2016-06-12 07:24
就如同当年我在16K内存的袖珍计算机上用BASIC编程,对每行语句都“斤斤计较”一样。
1