x86 32-বিট মেশিন কোড (32-বিট পূর্ণসংখ্যা): 17 বাইট।
(ডিএফ = 1 কলিং কনভেনশন সহ 32-বিট বা 64-বিটের 16 বাইট সহ নীচের অন্যান্য সংস্করণগুলিও দেখুন))
কলার আউটপুট বাফারের শেষের পয়েন্টার সহ রেজিস্টারগুলিতে অর্গগুলি পাস করে ( আমার সি উত্তরটির মতো ; এটি অ্যালগোরিদমের ন্যায়সঙ্গতকরণ এবং ব্যাখ্যা করার জন্য দেখুন gl_itoa
) গ্লিবসি অভ্যন্তরীণ এটি করে , সুতরাং এটি কেবল কোড-গল্ফের পক্ষে অনুমোদিত নয়। আরগ-পাসিং রেজিস্টারগুলি x86-64 সিস্টেম ভি এর কাছাকাছি, EDX এর পরিবর্তে EAX এ আমাদের একটি আরগ নেই।
ফিরে আসার পরে, ইডিআই আউটপুট বাফারে 0 টিমিনেটেড সি স্ট্রিংয়ের প্রথম বাইটে নির্দেশ করে। সাধারণ রিটার্ন-ভ্যালু রেজিস্ট্রার হ'ল ইএএক্স / আরএক্স, তবে সমাবেশের ভাষায় আপনি যে কোনও কলিং কনভেনশন কোনও ফাংশনের জন্য সুবিধাজনক হিসাবে ব্যবহার করতে পারেন। ( xchg eax,edi
শেষে 1 বাইট যুক্ত হবে)
কলার একটি সুস্পষ্ট দৈর্ঘ্য গণনা করতে পারে যদি এটি চায়, থেকে buffer_end - edi
। তবে আমি মনে করি না যে ফাংশনটি শুরু + সমাপ্তি পয়েন্টার বা পয়েন্টার + দৈর্ঘ্য উভয়ই প্রদান না করে আমরা টার্মিনেটর বাদ দিয়ে ন্যায্যতা প্রমাণ করতে পারি। এটি এই সংস্করণে 3 বাইট সাশ্রয় করবে, তবে আমি এটি ন্যায়সঙ্গত বলে মনে করি না।
- EAX = n = ডিকোড করার জন্য সংখ্যা। (কারণ
idiv
। অন্যান্য আরগগুলি অন্তর্ভুক্ত অপারেশন নয়))
- EDI = আউটপুট বাফারের শেষ (
dec edi
64৪ -বিট সংস্করণ এখনও ব্যবহার করে , তাই অবশ্যই কম 4GiB এ থাকা উচিত)
- ESI / RSI = দেখার টেবিল, ওরফে LUT। আঁটসাঁট না
- ইসিএক্স = টেবিলের = দৈর্ঘ্যের দৈর্ঘ্য। আঁটসাঁট না
nasm -felf32 ascii-compress-base.asm -l /dev/stdout | cut -b -30,$((30+10))-
(মন্তব্যগুলি সঙ্কুচিত করার জন্য হাত সম্পাদনা করা হয়েছে, লাইন নম্বরটি অদ্ভুত)
32-bit: 17 bytes ; 64-bit: 18 bytes
; same source assembles as 32 or 64-bit
3 %ifidn __OUTPUT_FORMAT__, elf32
5 %define rdi edi
6 address %define rsi esi
11 machine %endif
14 code %define DEF(funcname) funcname: global funcname
16 bytes
22 ;;; returns: pointer in RDI to the start of a 0-terminated string
24 ;;; clobbers:; EDX (tmp remainder)
25 DEF(ascii_compress_nostring)
27 00000000 C60700 mov BYTE [rdi], 0
28 .loop: ; do{
29 00000003 99 cdq ; 1 byte shorter than xor edx,edx / div
30 00000004 F7F9 idiv ecx ; edx=n%B eax=n/B
31
32 00000006 8A1416 mov dl, [rsi + rdx] ; dl = LUT[n%B]
33 00000009 4F dec edi ; --output ; 2B in x86-64
34 0000000A 8817 mov [rdi], dl ; *output = dl
35
36 0000000C 85C0 test eax,eax ; div/idiv don't write flags in practice, and the manual says they're undefined.
37 0000000E 75F3 jnz .loop ; }while(n);
38
39 00000010 C3 ret
0x11 bytes = 17
40 00000011 11 .size: db $ - .start
অবাক করা বিষয় যে মূলত কোনও গতি / আকারের ট্রেডঅফ সহ সহজতম সংস্করণটি সবচেয়ে ছোট, তবে std
/ বর্ধমান ক্রমে cld
2 বাইট ব্যবহার stosb
করতে এবং এখনও সাধারণ ডিএফ = 0 কলিং কনভেনশন অনুসরণ করে। (এবং স্টোর করার পরে স্টস হ্রাস পায় , লুপের প্রস্থানে পয়েন্টারটি একটি বাইটকে খুব কম দেখায় , আমাদের চারপাশে কাজ করার জন্য অতিরিক্ত বাইট খরচ করে))
সংস্করণ:
আমি 4 টি উল্লেখযোগ্যভাবে পৃথক বাস্তবায়ন ট্রিকগুলি নিয়ে এসেছি (সাধারণ mov
লোড / স্টোর (উপরে) ব্যবহার করে lea
/ / movsb
(ঝরঝরে তবে অনুকূল নয়) ব্যবহার করে xchg
/ xlatb
/ stosb
/ ব্যবহার করে xchg
এবং একটি যা ওভারল্যাপিং-নির্দেশিকা হ্যাকের সাহায্যে লুপে প্রবেশ করে below নীচের কোডটি দেখুন) । 0
আউটপুট স্ট্রিং টার্মিনেটর হিসাবে অনুলিপি করার জন্য শেষেরটির অনুসন্ধানের টেবিলটিতে একটি অনুসরণ করা দরকার , সুতরাং আমি এটিকে +1 বাইট হিসাবে গণনা করছি। 32/64-বিট (1-বাইট inc
বা না) উপর নির্ভর করে এবং আমরা কলার ডিএফ = 1 ( stosb
অবতরণ) নির্ধারণ করতে পারি বা যাই হোক না কেন, বিভিন্ন সংস্করণ সংক্ষিপ্ততম (এর জন্য আবদ্ধ) রয়েছে।
ডিএফ = 1 অবতরণ ক্রমে সঞ্চয় করতে এটি xchg / stosb / xchg এ একটি জয় করে তোলে, তবে কলার প্রায়শই তা চায় না; এটি কঠোরভাবে ন্যায়সঙ্গত উপায়ে কলারের কাছে অফলোডিংয়ের কাজ মনে হচ্ছে। (কাস্টম আরগ-পাসিং এবং রিটার্ন-ভ্যালু রেজিস্টারগুলির মতো নয়, যার জন্য সাধারণত কোনও এসএম কলারের কোনও অতিরিক্ত কাজ ব্যয় হয় না But) তবে 64৪-বিট কোডে cld
/ / এ scasb
কাজ করে inc rdi
, আউটপুট পয়েন্টারটি ৩২-বিটে ছাঁটাই করা এড়িয়ে চলে, তাই এটি কখনও কখনও 64-বিট-ক্লিন ফাংশনগুলিতে ডিএফ = 1 সংরক্ষণে অসুবিধা। । (স্ট্যাটিক কোড / ডাটাতে নির্দেশকগুলি লিনাক্সে x86-64 নন-পিআইই এক্সিকিউটেবলের 32-বিট হয় এবং সর্বদা লিনাক্স x32 এবিআইতে থাকে, তাই 32-বিট পয়েন্টার ব্যবহার করে একটি x86-64 সংস্করণ কিছু ক্ষেত্রে ব্যবহারযোগ্য is) যাইহোক, এই ইন্টারঅ্যাকশনটি প্রয়োজনীয়তার বিভিন্ন সংমিশ্রণগুলিকে দেখতে আকর্ষণীয় করে তোলে।
- এন্ট্রি / প্রস্থান কলিং কনভেনশনে একটি ডিএফ = 0 সহ আইএ 32:: 17 বি (
nostring
) ।
- আইএ 32: 16 বি (একটি ডিএফ = 1 কনভেনশন সহ:
stosb_edx_arg
বা skew
) ; বা ইনকামিং ডিএফ = ডন্টকেয়ার সহ এটি সেট রেখে: 16 + 1 বিstosb_decode_overlap
বা 17 বিstosb_edx_arg
- x86-64 64৪-বিট পয়েন্টার সহ, এবং প্রবেশ / প্রস্থান কলিং কনভেনশনে একটি ডিএফ = 0: 17 + 1 বাইট (
stosb_decode_overlap
) , 18 বি ( stosb_edx_arg
বা skew
)
x86-64 64৪-বিট পয়েন্টার সহ, অন্যান্য ডিএফ হ্যান্ডলিং: 16 বি (ডিএফ = 1 skew
) , 17 বি ( পরিবর্তে nostring
ডিএফ = 1 সহ ) ব্যবহার scasb
করুন dec
। 18 বি ( stosb_edx_arg
3 বাইট সহ ডিএফ = 1 সংরক্ষণ করা inc rdi
)।
অথবা যদি আমরা স্ট্রিংয়ের আগে 1 বাইটে পয়েন্টারটি ফেরত দেওয়ার অনুমতি দিই, 15 বি ( শেষটিstosb_edx_arg
ছাড়াই inc
)। সমস্ত আবার কল করতে এবং বিভিন্ন বেস / টেবিলের সাহায্যে বাফারে আরও একটি স্ট্রিং প্রসারিত করার জন্য প্রস্তুত ... তবে আমরা যদি একটি টার্মিনেটিং সংরক্ষণ না করি তবে এটি আরও অর্থপূর্ণ হবে 0
এবং আপনি ফাংশন বডিটিকে একটি লুপের ভিতরে রেখে দিতে পারেন যাতে এটি সত্যিই একটি পৃথক সমস্যা
x86-64 সাথে 32-বিট আউটপুট পয়েন্টার, ডিএফ = 0 কলিং কনভেনশন: -৪-বিট আউটপুট পয়েন্টারের চেয়ে কোনও উন্নতি হয়নি, তবে nostring
এখন 18 বি ( ) সম্পর্ক রয়েছে।
- 32-বিট আউটপুট পয়েন্টার সহ x86-64: সেরা 64-বিট পয়েন্টার সংস্করণগুলির তুলনায় কোনও উন্নতি হয়নি, সুতরাং 16 বি (ডিএফ = 1
skew
)। অথবা ডিএফ = 1 সেট এবং এটি জন্য ত্যাগ করেন, তখন 17b থেকে skew
সঙ্গে std
কিন্তু cld
। বা 17 + + 1B জন্য stosb_decode_overlap
সঙ্গে inc edi
পরিবর্তে শেষে cld
/ scasb
।
একটি ডিএফ = 1 কলিং কনভেনশন সহ: 16 বাইট (আইএ 32 বা x86-64)
ইনপুটটিতে DF = 1 প্রয়োজন, সেট করে ছেড়ে দেয়। সবেমাত্র প্রশ্রয়যোগ্য , কমপক্ষে প্রতি-কার্য ভিত্তিতে। উপরের সংস্করণ হিসাবে একই জিনিস, কিন্তু এক্সএলএটিবি এর আগে / পরে আ.লীগের বাকী / বাইরে থাকা (বেস হিসাবে আর / ইবিএক্সের সাথে সারণী সন্ধান) এবং STOSB ( *output-- = al
) পেতে xchg সহ ।
একটি স্বাভাবিক ডিএফ = 0 এন্ট্রি / প্রস্থান সম্মেলন সঙ্গে, / / সংস্করণ 32 এবং 64-বিট কোডের জন্য 18 বাইট, এবং 64-বিট পরিষ্কার (ক 64-বিট আউটপুট পয়েন্টার সাথে কাজে)।std
cld
scasb
নোট করুন যে ইনপুট আরগগুলি টেবিলের জন্য (জন্য xlatb
) আরবিএক্স সহ বিভিন্ন রেজিস্টারে রয়েছে । এছাড়াও লক্ষ করুন যে এই লুপটি AL সঞ্চয় করে শুরু হয় এবং শেষ চরটি এখনও সংরক্ষণ না করে শেষ হয় (অতএব mov
শেষে)। সুতরাং লুপটি অন্যের সাথে তুলনামূলকভাবে "স্কিউড", তাই নামটি।
;DF=1 version. Uncomment std/cld for DF=0
;32-bit and 64-bit: 16B
157 DEF(ascii_compress_skew)
158 ;;; inputs
159 ;; O in RDI = end of output buffer
160 ;; I in RBX = lookup table for xlatb
161 ;; n in EDX = number to decode
162 ;; B in ECX = length of table = modulus
163 ;;; returns: pointer in RDI to the start of a 0-terminated string
164 ;;; clobbers:; EDX=0, EAX=last char
165 .start:
166 ; std
167 00000060 31C0 xor eax,eax
168 .loop: ; do{
169 00000062 AA stosb
170 00000063 92 xchg eax, edx
171
172 00000064 99 cdq ; 1 byte shorter than xor edx,edx / div
173 00000065 F7F9 idiv ecx ; edx=n%B eax=n/B
174
175 00000067 92 xchg eax, edx ; eax=n%B edx=n/B
176 00000068 D7 xlatb ; al = byte [rbx + al]
177
178 00000069 85D2 test edx,edx
179 0000006B 75F5 jnz .loop ; }while(n = n/B);
180
181 0000006D 8807 mov [rdi], al ; stosb would move RDI away
182 ; cld
183 0000006F C3 ret
184 00000070 10 .size: db $ - .start
একটি অনুরূপ নন-স্কিউড সংস্করণ ইডিআই / আরডিআইকে ওভারশুট করে এবং তারপরে এটি ঠিক করে দেয়।
; 32-bit DF=1: 16B 64-bit: 17B (or 18B for DF=0)
70 DEF(ascii_compress_stosb_edx_arg) ; x86-64 SysV arg passing, but returns in RDI
71 ;; O in RDI = end of output buffer
72 ;; I in RBX = lookup table for xlatb
73 ;; n in EDX = number to decode
74 ;; B in ECX = length of table
75 ;;; clobbers EAX,EDX, preserves DF
76 ; 32-bit mode: a DF=1 convention would save 2B (use inc edi instead of cld/scasb)
77 ; 32-bit mode: call-clobbered DF would save 1B (still need STD, but INC EDI saves 1)
79 .start:
80 00000040 31C0 xor eax,eax
81 ; std
82 00000042 AA stosb
83 .loop:
84 00000043 92 xchg eax, edx
85 00000044 99 cdq
86 00000045 F7F9 idiv ecx ; edx=n%B eax=n/B
87
88 00000047 92 xchg eax, edx ; eax=n%B edx=n/B
89 00000048 D7 xlatb ; al = byte [rbx + al]
90 00000049 AA stosb ; *output-- = al
91
92 0000004A 85D2 test edx,edx
93 0000004C 75F5 jnz .loop
94
95 0000004E 47 inc edi
96 ;; cld
97 ;; scasb ; rdi++
98 0000004F C3 ret
99 00000050 10 .size: db $ - .start
16 bytes for the 32-bit DF=1 version
আমি এর অভ্যন্তরীণ লুপের বডি হিসাবে lea esi, [rbx+rdx]
/ এর বিকল্প সংস্করণ চেষ্টা করেছি movsb
। (আরএসআই প্রতিটি পুনরাবৃত্তি পুনরায় সেট করা হয় তবে আরডিআই হ্রাস পায়)। তবে এটি টার্মিনেটরের জন্য জোর-শূন্য / স্টস ব্যবহার করতে পারে না, সুতরাং এটি 1 বাইট আরও বড়। (এবং এটি এলইএতে কোনও রেক্স উপসর্গ ব্যতিরেকে অনুসন্ধানের টেবিলের জন্য পরিষ্কার নয়))
সুস্পষ্ট দৈর্ঘ্য এবং একটি 0 টার্মিনেটর সহ LUT : 16 + 1 বাইট (32-বিট)
এই সংস্করণটি ডিএফ = 1 সেট করে এবং এটি সেভাবে ছেড়ে যায়। আমি মোট বাইট গণনার অংশ হিসাবে প্রয়োজনীয় অতিরিক্ত LUT বাইট গণনা করছি।
এখানে শীতল কৌশলটি একই বাইট দুটি ভিন্ন উপায়ে ডিকোড করছে । আমরা বাকী = বেস এবং ভাগফল = ইনপুট নম্বর সহ লুপের মাঝখানে পড়ি, এবং 0 টার্মিনেটরটিকে জায়গায় অনুলিপি করব।
ফাংশনের মাধ্যমে প্রথমবারের মতো লুপের প্রথম 3 বাইট একটি এলইএর জন্য ডিসপয়েন্ট 2 এর হাই বাইট হিসাবে গ্রাস করা হয়। এলইএটি বেস (মডুলাস) কে EDX এ অনুলিপি করে, idiv
পরবর্তী পুনরাবৃত্তির জন্য অবশিষ্ট উত্পাদন করে।
২ য় বাইট idiv ebp
হ'ল FD
, যা এই ফাংশনটিতে কাজ করা উচিত সেই std
নির্দেশের জন্য অপকড । (এটি একটি ভাগ্যবান আবিষ্কারের ছিল। আমি এই সময়ে খুঁজছি হয়েছে div
আগে, যা নিজেই থেকে আলাদা idiv
ব্যবহার /r
এর 2nd বাইট ModRM মধ্যে বিট। div epb
যেমন decodes cmc
, যা সহায়ক কিন্তু নিরীহ নয়। কিন্তু idiv ebp
আমরা আসলে অপসারণ করতে পারেন std
উপর থেকে ফাংশন।)
নোট করুন ইনপুট রেজিস্টারগুলি আবার পার্থক্য: বেসের জন্য ইবিপি।
103 DEF(ascii_compress_stosb_decode_overlap)
104 ;;; inputs
105 ;; n in EAX = number to decode
106 ;; O in RDI = end of output buffer
107 ;; I in RBX = lookup table, 0-terminated. (first iter copies LUT[base] as output terminator)
108 ;; B in EBP = base = length of table
109 ;;; returns: pointer in RDI to the start of a 0-terminated string
110 ;;; clobbers: EDX (=0), EAX, DF
111 ;; Or a DF=1 convention allows idiv ecx (STC). Or we could put xchg after stos and not run IDIV's modRM
112 .start:
117 ;2nd byte of div ebx = repz. edx=repnz.
118 ; div ebp = cmc. ecx=int1 = icebp (hardware-debug trap)
119 ;2nd byte of idiv ebp = std = 0xfd. ecx=stc
125
126 ;lea edx, [dword 0 + ebp]
127 00000040 8D9500 db 0x8d, 0x95, 0 ; opcode, modrm, 0 for lea edx, [rbp+disp32]. low byte = 0 so DL = BPL+0 = base
128 ; skips xchg, cdq, and idiv.
129 ; decode starts with the 2nd byte of idiv ebp, which decodes as the STD we need
130 .loop:
131 00000043 92 xchg eax, edx
132 00000044 99 cdq
133 00000045 F7FD idiv ebp ; edx=n%B eax=n/B;
134 ;; on loop entry, 2nd byte of idiv ebp runs as STD. n in EAX, like after idiv. base in edx (fake remainder)
135
136 00000047 92 xchg eax, edx ; eax=n%B edx=n/B
137 00000048 D7 xlatb ; al = byte [rbx + al]
138 .do_stos:
139 00000049 AA stosb ; *output-- = al
140
141 0000004A 85D2 test edx,edx
142 0000004C 75F5 jnz .loop
143
144 %ifidn __OUTPUT_FORMAT__, elf32
145 0000004E 47 inc edi ; saves a byte in 32-bit. Makes DF call-clobbered instead of normal DF=0
146 %else
147 cld
148 scasb ; rdi++
149 %endif
150
151 0000004F C3 ret
152 00000050 10 .size: db $ - .start
153 00000051 01 db 1 ; +1 because we require an extra LUT byte
# 16+1 bytes for a 32-bit version.
# 17+1 bytes for a 64-bit version that ends with DF=0
এই ওভারল্যাপিং ডিকোড ট্রিকটি এর সাথেও ব্যবহার করা যেতে পারে cmp eax, imm32
: কার্যকরভাবে 4 বাইট কার্যকরভাবে এগিয়ে যেতে কেবল 1 বাইট লাগে, কেবল ক্লাববারিং পতাকা। (সিপিইউগুলিতে পারফরম্যান্সের জন্য এটি ভয়ানক যা এল 1 আই ক্যাশে বিটিডাব্লু নির্দেশের সীমানা চিহ্নিত করে))
তবে এখানে, আমরা একটি রেজিস্টার অনুলিপি করতে এবং লুপটিতে লাফাতে 3 বাইট ব্যবহার করছি। এটি সাধারণত 2 + 2 (মুভি + জেএমপি) নেবে এবং XLATB এর পরিবর্তে STOS এর আগে ডানদিকে লুপ করতে দেয়। তবে তারপরে আমাদের একটি আলাদা এসটিডি দরকার, এবং এটি খুব আকর্ষণীয় হবে না।
এটি অনলাইন চেষ্টা করুন! (একটি _start
কলার যা ফলাফলটিতে ব্যবহার sys_write
করে)
এটি ডিবাগিংয়ের অধীনে চালনা করা strace
বা আউটপুট হেক্সডাম্পের পক্ষে সবচেয়ে ভাল, যাতে আপনি যাচাই করতে পারেন যে \0
সঠিক জায়গায় কোনও টার্মিনেটর রয়েছে এবং এই জাতীয় কিছু। তবে আপনি এটি আসলে কাজ করতে পারবেন এবং এর ইনপুটটির AAAAAACHOO
জন্য উত্পাদন করতে পারেন
num equ 698911
table: db "CHAO"
%endif
tablen equ $ - table
db 0 ; "terminator" needed by ascii_compress_stosb_decode_overlap
(আসলে xxAAAAAACHOO\0x\0\0...
, কারণ আমরা আগেই 2 বাইট থেকে ডাম্পিং করছি আউট একটি নির্দিষ্ট দৈর্ঘ্যের বাফার। সুতরাং আমরা দেখতে পারি ফাংশন লিখেছেন বাইট এটা অনুমিত ছিল না কোনো বাইট এটা থাকা উচিত নয় উপর ধাপ। দ্য ফাংশনটিতে প্রারম্ভিক-নির্দেশকটি ছিল দ্বিতীয়-শেষ x
চরিত্রটি, যা জেরো অনুসরণ করেছিল)