8088组装件,IBM PC DOS, 164 159 156 155字节
二进制:
00000000: d1ee 8a0c 03f1 53fd ac3a d075 0343 e2f7 ......S..:.u.C..
00000010: 85db 741c 5f8a d043 f6c3 0174 0a57 bd64 ..t._..C...t.W.d
00000020: 0155 83eb 0374 0957 bd5d 0155 4b4b 75f7 .U...t.W.].UKKu.
00000030: 8ad0 2c2f 7213 518a f0b0 24b1 31bf 6a01 ..,/r.Q...$.1.j.
00000040: fcf2 aefe ce75 fa59 57e2 bc5a 85d2 740c .....u.YW..Z..t.
00000050: b409 cd21 b220 b402 cd21 ebef c364 6f75 ...!. ...!...dou
00000060: 626c 6524 7472 6970 6c65 246f 6824 6f6e ble$triple$oh$on
00000070: 6524 7477 6f24 7468 7265 6524 666f 7572 e$two$three$four
00000080: 2466 6976 6524 7369 7824 7365 7665 6e24 $five$six$seven$
00000090: 6569 6768 7424 6e69 6e65 24 eight$nine$
xxd -r
从上面构建并测试可执行文件,或下载PHONE.COM。
未组装的清单:
D1 EE SHR SI, 1 ; point SI to DOS PSP (80H) for input string
8A 0C MOV CL, BYTE PTR[SI] ; load input string length into CX
03 F1 ADD SI, CX ; move SI to end of input
53 PUSH BX ; push a 0 to signal end of output stack
CHAR_LOOP:
FD STD ; set LODS direction to reverse
AC LODSB ; load next char from [SI] into AL, advance SI
3A D0 CMP DL, AL ; is it same as previous char?
75 03 JNZ NEW_CHAR ; if not, it's a different char
43 INC BX ; otherwise it's a run, so increment run length
E2 F7 LOOP CHAR_LOOP ; move on to next char
NEW_CHAR:
85 DB TEST BX, BX ; is there a run greater than 0?
74 1C JZ GET_WORD ; if not, look up digit name
5F POP DI ; get name for the current digit
8A D0 MOV DL, AL ; save current char in DL
43 INC BX ; adjust run count (BX=1 means run of 2, etc)
F6 C3 01 TEST BL, 1 ; is odd? if so, it's a triple
74 0A JZ IS_DBL ; is even, so is a double
57 PUSH DI ; push number string ("one", etc) to stack
BD 0164 MOV BP, OFFSET T ; load "triple" string
55 PUSH BP ; push to stack
83 EB 03 SUB BX, 3 ; decrement run count by 3
74 09 JZ GET_WORD ; if end of run, move to next input char
IS_DBL:
57 PUSH DI ; push number string to stack
BD 015D MOV BP, OFFSET D ; load "double" string
55 PUSH BP ; push to stack
4B DEC BX ; decrement by 2
4B DEC BX
75 F7 JNZ IS_DBL ; if not end of run, loop double again
GET_WORD:
8A D0 MOV DL, AL ; save current char into DL
2C 2F SUB AL, '0'-1 ; convert ASCII char to 1-based index
72 13 JB NOT_FOUND ; if not a valid char, move to next
51 PUSH CX ; save outer loop counter
8A F0 MOV DH, AL ; DH is the index to find, use as scan loop counter
B0 24 MOV AL, '$' ; word string is $ delimited
B1 31 MOV CL, 031H ; search through length of word data (49 bytes)
BF 016A MOV DI, OFFSET W ; reset word data pointer to beginning
FC CLD ; set DF to scan forward for SCAS
SCAN_LOOP:
F2/ AE REPNZ SCASB ; search until delimiter '$' is found in [DI]
FE CE DEC DH ; delimiter found, decrement counter
75 FA JNZ SCAN_LOOP ; if counter reached 0, index has been found
59 POP CX ; restore outer loop position
57 PUSH DI ; push string on stack
NOT_FOUND:
E2 BC LOOP CHAR_LOOP ; move to next char in input
OUTPUT_STACK:
5A POP DX ; get string from top of stack
85 D2 TEST DX, DX ; it is the last?
74 0C JZ EXIT ; if so, exit
B4 09 MOV AH, 09H ; DOS display string function
CD 21 INT 21H ; write string to console
B2 20 MOV DL, ' ' ; load space delimiter
B4 02 MOV AH, 02H ; DOS display char function
CD 21 INT 21H ; write char to console
EB EF JMP OUTPUT_STACK ; continue looping
EXIT:
C3 RET ; return to DOS
D DB "double$"
T DB "triple"
W DB "$oh$","one$","two$","three$","four$","five$","six$","seven$","eight$","nine$"
TL; DR:
输入字符串从右到左读取,以便更轻松地查找三元组。输出被推入x86堆栈以简化显示顺序的反转,也便于重新排列“ double”和“ triple”两个单词,使其位于数字的名称之前。
如果下一位数字与最后一位数字不同,则在单词列表中查找该名称并将其压入堆栈。由于在机器代码中没有“变长字符串的索引数组”的正式概念,因此对单词列表进行扫描i
(单词的索引)次数,$
以查找字符串定界符()以找到相应的单词。有用的是,x86确实有一对简短的指令(REPNZ SCASB
类似于memchr()
C语言),从而简化了这一过程(感谢CISC!)。
如果数字与前一个数字相同,则“行程”长度的计数器增加,并继续在输入上向左循环。运行结束后,将从堆栈中获取数字名称,因为每个分组都需要将其放置在“双精度”或“三重”之后。如果游程长度为奇数(游程长度为> 1
),则将数字名称和字符串“ triple”(三串)压入堆栈,并将游程长度减少3。由于游程长度现在为偶数,因此该步骤重复“双”,直到游程长度为0。
当输入字符串到达末尾时,堆栈将转储,每个保存的字符串都以相反的顺序写入屏幕。
输入/输出:
一个独立的PC DOS可执行文件,从命令行输出输入到控制台。
下载并测试PHONE.COM。