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

大家来写写匯编小玩意(二)

Valenciax 发布于 2016-06-20 13:31, 9948 次点击
在别的论坛看见一些有点意思的题目,貼这里让大家练练手。

题目一:
从键盘接收任意字符(长度<=20) ,将其中小写字母以递减方式排序(z......a),
并一次性显示其前后字符,每项资料以空白分隔。
例1:-
输入:12aBb23cD
输出:3cD Bb2 2aB
例2:-
a12z50yw9b8cn34x
2z5 0yw 4x  yw9 cn3 8cn 9b8  a1
另外,因为程序比较简单,希望大家以最短代码完成(不是程式大小),
意即用多少行代码写成。
下面是我用54行完成任务,嗯,应该还有优化的空间。
只有本站会员才能查看附件,请 登录


题目二:
已知BUF1中有N1个从小到大的顺序排列互不相等的字符号数,
BUF2中有N2个从小到大的顺序排列互不相等的字符号数,
编写程序将BUF1和BUF2中的数合并到BUF3中,使在BUF3中存放的数互不相等(意即排除相等),
且从小到大的顺序排列。
程式要求:
1.不能另写排序代码(因已排序),先印出buf1及buf2,再印出buf3,均以十进制显示。
2.以最短代码完成(不是程式大小)。

测试数据:
 buf1 db 3,12,14,15,17,19,22,23,88,99,101,105,106,109,202
 buf2 db 1,3,5,6,10,12,14,18,19,21,22,45,101,103,105,106,109

列印结果:
1 3 5 6 10 12 14 15 17 18 19 21 22 23 45 88 99 101 103 105 106 109 202

下面是我用91行完成任务,应该可以再短些。
只有本站会员才能查看附件,请 登录


有趣兴可以写写,贴出代码,或者说说想法也可以。

[此贴子已经被作者于2016-6-21 20:41编辑过]

17 回复
#2
wmf20142016-06-21 19:21
回复 楼主 Valenciax
先就这样吧。交第一题作业,包含5行伪代码共55行,抽空再优化下。
程序代码:
code segment
assume cs:code,ds:code,es:code,ss:code
org 100h
start:    jmp begin
buf    db 80 dup(0)
rmov:    cmp dl,'a'
    jb  mlp3
    cmp dl,'z'
    ja  mlp3
    push si          ;比较大小,找到显示数据插入点,起始位置si,数据顺序是bl bh dl dh,分别位于bx,dx中
    mov cl,dl
mlp1:    cmp cl,[si+2]
    jbe mlp2
    mov di,si
    xor cl,cl        ;找到插入点至di
mlp2:    mov ah,[si]
    add si,4
    cmp ah,'$'
    jnz mlp1         ;判断是否找到字符串结束标志$
    mov cx,si
    sub cx,di
    mov di,si
    sub si,4
    sub cx,3
    std
    rep movsb
    inc si
    cmp dh,13
    jnz mlp4
    mov dh,20h
mlp4:    mov [si],bx
    mov [si+2],dx    ;插入当前显示的数据
    pop si
mlp3:    ret
begin:    mov bx,2020h
    mov dx,bx
    mov si,offset buf
    mov word ptr[si],'$'
blp1:    mov ah,1
    int 21h
    mov bh,dl
    mov dl,dh
    mov dh,al
    call rmov
    cmp al,13
    jz blp2
    cmp al,'$'
    jnz blp1
blp2:    mov dx,si
    mov ah,9
    int 21h
    mov ax,4c00h
    int 21h
code ends
end start


[此贴子已经被作者于2016-6-22 07:39编辑过]

#3
Valenciax2016-06-22 08:09
回复 2楼 wmf2014
灰常感谢wmf2014班竹参与游戏!
题一,我又想到了优化方法,缩减到42行…
今早起床洗脸时,忽然脑海又闪出另一种完全不同的方法,苦哈哈的敲了键盘30分钟,居然才减了区区一行到41行,好象意思不大,但因为是两种方法,回头整理一下再发上来。
#4
Valenciax2016-06-22 09:34
回复 2楼 wmf2014
汇编果然千变万化,班竹的想法是另辟蹊径,也挺巧妙,利用栈操作,看来反倒不像我的一板一眼。

新的42bytes方案由54bytes那个优化的,就直接说说42bytes吧。

这里用最传统的int21h,0Ah函数读入字符串,取了长度后,直接在输入字符串前后补上空白。利用lodsb读入al,其中比较a-z用了这个方法
sub al,'a'  ;减去ascii, a ->0 .... z ->25
cmp al,25 ;比较是否小写a-z内
若读入'a',减去'a',则字符'a'-'z'会转为值0-25,那么比较一次25就限制在a-z内,不用再2次比较。
mov eax,[si-2] ;取输入区4字节
这里利用32bit代码,直接取该字符前一位置之后4字节入EAX
再由输出区
1)
mov ebx,[di]取输出区4字节
若这EBX为空白,则该输出区为空白,直接放入EAX值,另把第4字节置空白
若EBX不为空白,比较cmp ah,bh,这是比较 [小写] 谁大,
若ah大,则新值置入该[输出区],交换EAX和EBX,新的EAX就是原来较小的值
增加输出区地址add di,4,再跳到1)从新和下值比较,如此就相当于泡泡法排序。
编写下来是42行,代码为了压缩空间,用的是比较新式的.MODEL TINY格式,
须用masm 6.x编译,直接转为

ML /AT ex2.asm

程序代码:

.MODEL TINY
.386   
.code
ORG 100H
start:    mov dx,offset inputBuf
    mov ax,0c0ah ;清除键盘缓冲,并输入函数
    int 21h
    mov si,offset inputbuf + 2
    xor ecx,ecx
    mov cl,[si-1] ;取输入数
    jcxz quit ;没输入,离开
    mov byte ptr [esi+ecx],20h   ;0d->20h 加空白于后
    mov byte ptr [si-1],20h    ;size->20h ;加空白于前
next:    mov di,offset headbuf + 2
    lodsb    ;取字符
    sub al,'a'  ;减去ascii, a ->0 .... z ->25
    cmp al,25 ;比较是否小写a-z内
    ja next9    ;不是
    mov eax,[si-2] ;取输入区4字节
next1:    mov ebx,[di] ;取输出区4字节
    cmp ebx,20202020h ;是否空白
    jnz next2 ;不是
    stosd ;空白则送到输出区
    mov byte ptr [di-1],20h ;遮罩第4字节成空白
    jmp short next9 ;回圈
next2:    cmp ah,bh ;比较 [该小写] 谁大
    ja next4 ;大于
next3:    add di,4 ;少于则下一个
    jmp short next1
next4:    stosd ;取代输出区项目
    mov byte ptr [di-1],20h ;遮罩第4字节空白
    xchg ebx,eax ;交换二值,eax=原来输出区资料
    jmp short next1 ;再和下一资料比较,即泡泡排序法
next9:    loop next ;回圈 ;  因lodsb 已自动 inc si ;下一输入字节
    mov dx,offset headbuf ;----印结果----
    mov ah,9
     int 21h
quit:    mov ah,4ch ;离开
    int 21h

 headbuf db 0dh,0ah, 4 * 20 dup (20h),'$'

 inputBuf db 21,0,21 dup(0)
end start  ;-----------代码结束---------------


[此贴子已经被作者于2016-6-22 09:38编辑过]

#5
wmf20142016-06-22 13:44
另一种算法,36行代码完成任务(应该还能优化),我没有32位masm:
程序代码:
code segment
assume cs:code,ds:code,es:code,ss:code
org 100h
start:    mov di,offset buf+1
    mov ah,1
slp1:    int 21h
    stosb
    cmp al,0dh
    jnz slp1
    mov byte ptr[di-1],20h
    mov al,'z'
slp5:    mov di,offset buf+1
    mov cx,22
slp4:    repnz scasb
    cmp cx,0
    jz  slp2
    push ax
    push cx
    mov si,di
    sub si,2
    mov cx,4
    mov al,20h
slp3:    int 29h
    lodsb
    loop slp3
    pop cx
    pop ax
    loop slp4
slp2:    dec al
    cmp al,'a'
    jae slp5
    mov ax,4c00h
    int 21h
    buf db 22 dup(20h)
code ends
end start
#6
Valenciax2016-06-22 14:18
回复 5楼 wmf2014
班竹很厉害,写得挺不错的,学习了!
slp4:    repnz scasb
    cmp cx,0
    jz  slp2
这里或者可以优化一下,因为停下来一定是找到或找不到,找到zr或找不到nz
slp4:    repnz scasb
    jnz  slp2

若加入32bit写法
mov si,di
sub si,2
这两句可以归纳一句:
Lea si,[edi-2]


另外题目要求20字节内,这程式却没有限制

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

#7
wmf20142016-06-22 14:46
回复 6楼 Valenciax
我是做了实验才这样写得,scas不管什么情况只要出了rep都会造成zr标志.限制到20字节大概多2句,另这样写法允许显示$符号。Lea si,[edi-2]不需要32为asm,现在就可实现,少一句。

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

#8
Valenciax2016-06-22 15:02
回复 7楼 wmf2014
在cx次内,找不到会是nz, 找到是zr , cx的值并不决定找寻结果

只有本站会员才能查看附件,请 登录
#9
wmf20142016-06-22 17:03
回复 8楼 Valenciax
看了下第二题,觉得比第一个还容易些,用50句应该能解决。
你的scan指令用错。8088串指令都是针对si和di寻址的,不是dx,我实验结果如下图:
只有本站会员才能查看附件,请 登录
#10
Valenciax2016-06-22 17:30
回复 9楼 wmf2014
呵呵对不起,不是理解错,是di写错了dx
CX不能用来判别找到或找不到是因为有边界条件
若找寻的字符在最后,用cx来判就有问题

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


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


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

#11
wmf20142016-06-23 17:12
怎么没人做第二题呢?大概还是冷清的原因。
根据题意,这题首先要解决十六进制转十进制显示的问题。其次再是显示过滤后的数据。显示需要的数据真不需要排序,有两个方法解决:
1,查表法,由于是一个字节的数据,因此可以使用一个256字节的表格,通过一次性扫描,在表格里建立数据存在标志,显示是按顺序扫描表格只显示有标志的字节编号。优点是只需扫描一次,速度快,缺点是需要内存空间建立表格。
2,试探法,用一个计数器从0-255计数,逐个判断是否和数据缓冲里的数据相等,相等则显示,不等继续计数。优点是不需要多余内存空间,缺点是要扫描256次数据区。
下述用47行代码能满足楼主要求:
程序代码:
    code segment
    assume cs:code,ds:code,es:code,ss:code
    org 100h
start:    xor bl,bl
    cld
slp1:    mov al,bl
    mov di,offset buf1
    mov cx,offset buf3+1
    sub cx,di
    repnz scasb
    cmp cx,0
    jz slp2
    call hexdec
slp2:    cmp bl,255
    jz slp3
    inc bl
    jmp slp1
slp3:    mov ax,4c00h
    int 21h
hexdec:    pushf      ;一个字节的十六进制转换为十进制并显示出来,待转换数据在ax中
    push dx    ;根据本程序完全不需要push dx,但写函数起码的习惯是保存要用的寄存器
    push di
    mov di,offset buf3+3
    std
hdlp2:    mov byte ptr[di],20h
    xor ah,ah
    cmp ax,0
    jz  hdlp1  ;ax=0转换结束
    div buf4
    xchg al,ah
    add al,'0'
    stosb
    xchg al,ah
    jmp hdlp2
hdlp1:    mov ah,9
    mov dx,di
    int 21h
    pop di
    pop dx
    popf
    ret
    buf1 db 3,12,14,15,17,19,22,23,88,99,101,105,106,109,202
    buf2 db 1,3,5,6,10,12,14,18,19,21,22,45,101,103,105,106,109
    buf3 db 4 dup(20h),'$'
    buf4 db 10  ;除数
    code ends
    end start
#12
Valenciax2016-06-23 18:06
回复 11楼 wmf2014
厉害厉害,真是为题做码到了无所不用其极的地步!
根据班竹的代码,我做了些简化,共34行

程序代码:

    code segment
    assume cs:code,ds:code,es:code,ss:code
    org 100h
start: xor bl,bl
slp1:  mov al,bl
       mov di,offset buf1
       mov cx,offset buf_len
       repnz scasb
       jnz slp2
       call hexdec
       mov al,20h
       int 29h
slp2:  inc bl
       jnz slp1
slp3:  mov ax,4c00h
       int 21h
hexdec:mov cx,0 ;清0
       mov bp,10 ;除法准备
Pax1:  mov dx,0 ;清0
       div bp ;ax /10 ,若1234 ,除10后,dl得余数4,
       push dx ;保存, ax=1234,依次保存4,3,2,1
       inc cx ;累加个数
       or ax,ax ;是否已除尽
       jnz Pax1 ;不是,再除
Pax2:  pop ax ;后入先出,先印出第一数,然后第二....
       or al,30h ;转ascii
       int 29h
       loop Pax2 ;下一个
       ret
    buf1 db 3,12,14,15,17,19,22,23,88,99,101,105,106,109,202
    buf2 db 1,3,5,6,10,12,14,18,19,21,22,45,101,103,105,106,109
    buf_len equ $ - offset buf1
    code ends
    end start


[此贴子已经被作者于2016-6-23 18:09编辑过]

#13
wmf20142016-06-23 21:08
回复 12楼 Valenciax
厉害!
我崇拜算法,也喜欢技巧,但8088汇编已经nnnn年没弄了(早期最熟悉6502、z80汇编),不熟练就不能灵活使用技巧,向你学习。
#14
Valenciax2016-06-24 13:59
回复 13楼 wmf2014
虽然没碰过,但可以想象到,8BIT的6502、z80汇编,限制更多,带着铁铐跳舞,更考验程式师的技巧!
#15
Valenciax2016-07-25 16:31
忽发奇想,试试用几种高阶语言写写这两题,看哪种代码最短;
嗯,好像python效果不差,题一用了5行,题二用了2行,,应该还有更短的,大家试找一下,那种语言能更短。

题一
python
程序代码:

s=input('please input a string:')
s=' '+ s + ' '                  # add space
for x in range(122,96,-1):      # z(122)- a(97)
    y=s.find(chr(x),0)          # find x in string, if not found y=-1
    if y != -1: print(s[y-1:y+2],end=' ')     # print result   

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


题二
python

s=(list(set([3,12,14,15,17,19,22,23,88,99,101,105,106,109,202] + [1,3,5,6,10,12,14,18,19,21,22,45,101,103,105,106,109])))
if s.sort() !=-1: print(s)

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


[此贴子已经被作者于2016-7-27 12:05编辑过]

#16
Alar302016-10-21 10:26
不错不错哈。。。
#17
dongdon9232016-12-12 14:03
路过而学习了
#18
shanxidls2017-05-15 18:56
好贴!虽然看不懂,但我们这菜类就需要这些东西。希望多发此类代优秀好文档
1