1. Си / Говнокод #25449

    +1

    1. 1
    #define BSWAP16(x) ( (uint16_t)(((uint32_t)x)*(1 | 1 << 16) >> 8) )

    Запостил: j123123, 15 Марта 2019

    Комментарии (126) RSS

    • #define BSWAP16(x) (uint16_t)( (uint16_t)(x) * (1 | 1 << 16) >> 8)
      Ответить
      • На платформе с 16-битным интом не сработает, в отличие от оригинала.
        Ответить
        • А если в оригинал сунут число > 65536?
          Ответить
        • Разве на платформе с 16-битным интом оно не сломается еще на стадии (1 | 1 << 16) в том числе и на оригинале?

          Надо наверно так, чтоб уж наверняка:
          #define BSWAP16(x) (uint16_t)( (uint16_t)(x) * (1ULL | 1ULL << 16ULL) >> 8ULL)
          Ответить
          • По-моему 0x00010001 нагляднее этих сдвигов будет.
            Ответить
        • На платформе с 16-битным интом нету uint32_t
          Ответить
          • Почему? На платформах с 32-битным интом sizeof(long long) * CHAR_BIT может быть (и чаще всего так и есть) равным 64. Аналогично, на платформах с 16-битным интом uint32_t скорее всего будет синонимом unsigned long long.
            Ответить
            • потому что какие есть платформы с 16ти битным интом кроме борланд си?
              Ответить
              • Детали реализации меня не волнуют.
                Ответить
              • Watcom C.

                По крайней мере, в Open Watcom уже есть <stdint.h>.

                У x86 есть несколько инструкций для 32-битных чисел, они используют пару регистров (DX и AX, например).
                Ответить
                • нопример?
                  Ответить
                  • idiv, div, imul, mul
                    Ответить
                    • версии с 32х битными операндами появились в 286 или даже 386
                      в 8086 их не было
                      Ответить
                      • С парой регистров DX:AX всегда были.
                        Ответить
                        • а если вот я сейчас проверю что будет?
                          Ответить
                          • http://gabrielececchetti.it/Teaching/CalcolatoriElettronici/Docs/i8086_instruction_set.pdf
                            см. инструкции div, mul
                            Ответить
                            • Почему браузер в заголовке этой ссылки пишет «file://H:\Apps\devel\emu8086\documentation\8086 _instruction_set»?
                              Ответить
                              • Этот заголовок пишет не браузер, а плагин pdf.

                                Потому что автор оставил такие метаданные в pdf-файле. Он ещё написа́л, что автора зовут gabriele.
                                Ответить
                  • У MUL произведение 32-битное, у DIV делимое 32-битное.

                    Я ещё открыл для себя давно забытую инструкцию SHLD: сдвинуть содержимое регистра на N позиций влево, вдвинув в освободившиеся позиции биты из другого регистра. Есть аналогичная инструкция SHRD.

                    А ещё в плавпитухе есть 32-битное и 64-битное целое.
                    Ответить
                    • > в плавпитухе
                      Но как с целым-то с ним оперировать не получится.
                      Ответить
                      • Да, там всё через жопу: загружать в питушиный стек из оперативки через FILD, арифметика через FIADD, FISUB, FIMUL, FIDIV, FICOM, сохранение в оперативке из питушиного стека через FIST (кулак, ліл). Нужно ещё помнить, что в это же время нельзя работать с «много средства массовой информации удлинениями».

                        Но снимок в оперативке, сделанный инструкцией КУЛАК, выглядит так же, как обычное целое число.
                        Ответить
                        • Именнопо этому я за софтварную эмуляцию FPU
                          Ответить
                          • Для тебя много средства массовой информации удлинения важнее?
                            Ответить
                            • Не пониимаю как это связано с мягкими фракциями парящей точки модуля.

                              а, я понял. Ты про то что много средства массовой информации удлинения использовали один и тот же регистр с парящей точкой модуля?

                              Кажется это исправили в струящихся одна коммнда -- много сведений удлиненийя
                              Ответить
      • #define BSWAP16(x) (x ^ (uint16_t)(x << 8 | x >> 8) ^ x)
        Ответить
        • uint16_t x = 0xff00;
          x = BSWAP16(x++);
          Ответить
          • #define BSWAP16(x) ({uint16_t t = x; (uint16_t)((t & 0xFF00) >> 8 | t << 8);})
            Ответить
          • Зато красиво и симметрично. И гоатсе напоминает.
            #define BSWAP16(x) (uint16_t)(x ^ (x << 8 | x >> 8) ^ x)
            Ответить
            • #define BSWAP16(x) (uint16_t)(x ^ (x << 8 | x >> 8) ^ x)
              Сократил, проверь.
              Очевидно же, что если ксорить какое-то говно два раза с одним и тем же говном, то ничего не поменяется
              Ответить
    • : BSWAP16  ( u -- u )
          BASE @ >R  HEX
          S" FFFF AND 00010001 * 8 RSHIFT FFFF AND" EVALUATE
          R> BASE !
      ; IMMEDIATE
      Работает и в режиме компиляции (как макрос) и в режиме интерпретации.

      https://ideone.com/1KiivR
      Ответить
    • Байтоёбство без побитовых операций:
      #define BSWAP16(x) ({union{uint16_t u16; uint8_t u8[2];} t = {.u16 = x}; (union{uint16_t u16; uint8_t u8[2];}){.u8 = {t.u8[1], t.u8[0]}}.u16;})
      Ответить
    • ror ax, 8
      Именно поэтому я за "assembler".
      Ответить
      • rev16 r0, r0
        Именно поэтому я за "ARM".
        Ответить
      • xchg ah, al
        Ответить
        • Это в x86 валидная инструкция? О_о
          Ответить
          • Вполне
            Ответить
          • А что не так? ah, al, bh, bl, ch, cl, dh, dl в некоторых инструкциях могут использоваться как самостоятельные регистры.

            Есть инструкция xchg, ожидающая восьмибитные данные. Аргументами могут выступать два восьмибитных регистра либо восьмибитный регистр и указатель на память.
            Ответить
          • Ещё как, у XCHG есть несколько версий: однобайтовая с регисторм AX/EAX (КОП 90-9F), и с батом MOD/RM (КОП 86 -- размер операнда байт, 87 -- WORD/DWORD), что рождает кучу 2-хбайтовых NOP'ов типа XCHG AH, AH XCHG BX, BX и т.п. К сожалению XCHG REG, REG не попадает в множество ASCII :(
            Ответить
            • Зато XOR и SUB попадают. Можно эмулировать XCHG пачкой ксоров или вычитаний.
              Ответить
              • Во множество ASCII-кодов попадают только значения «регистр-память» байта mod R/M. Кобенации «регистр-регистр» за пределами ASCII-кодов.
                Ответить
                • Придумал, как обменять значения ah и al в ascii-кодах.
                  temp equ [bx+40h] ; Произвольный адрес в памяти.
                  ; Значение этой ячейки потом восстановится.
                  xor ah, temp; [email protected] ; в ah теперь ah XOR temp
                  xor temp, al; [email protected] ; в temp теперь temp XOR ah XOR al
                  xor al, temp; [email protected] ; в al теперь temp xor ah
                  xor al, temp; [email protected] ; в al теперь старое значение ah
                  xor ah, temp; [email protected] ; в ah теперь temp XOR al
                  xor temp, ah; [email protected] ; в ah теперь старое значение al
                  xor temp, al; [email protected] ; в temp теперь старое значение temp

                  Итого: [email protected]@[email protected]@[email protected]@[email protected]
                  21 байт. Длинновато вышло, зато в ASCII-кодах.
                  Ответить
                  • Ты же не будешь отрицать, что ты поехавший?
                    Ответить
                    • Братишка, давай я насру в уголочке в ASCII-кодах, программисты слетятся, тут мы их и прихлопнем?
                      Ответить
                  • Как ты это делаешь? Ты помнишь заизусть какие машинные коды соответствуют какой команде?
                    Ответить
                    • У меня шпаргалка есть:
                      http://govnokod.ru/15764#comment419684
                      Ответить
                      • Как ты понял, что, например, "@", соответствует именно "AX, [BX+SI+d8]"?
                        Ответить
                        • У меня тут @ —– это вообще произвольная константа. Вместо неё можно использовать любой октет, только во всей программе один и тот же.

                          [email protected] –— это ah, [bx+40h]
                          [email protected] –— это al, [bx+40h]
                          Ответить
                        • Или ты про материал по ссылке? Ну так mod-R/M можно собрать самому по битам, а для тупых даже опубликованы готовые сетки значений mod-R/M и s-i-b:
                          http://www.club155.ru/x86cmdformats-modrm
                          Ответить
                    • Тут самое сложное –— не запутаться, изобретая схему обмена значений. Ещё важно было не забыть восстановить значение ни в чём не повинной переменной, которое мы временно портили.
                      Ответить
                  • 5 байт:
                    PPDXD
                    push ax
                    push ax
                    inc sp
                    pop ax
                    inc sp
                    Ответить
                    • Ты реально аццкий сотона!
                      Ответить
                    • Твоим методом также можно вращать регистры BP, SI, DI, для которых у x86 не было половинок (bpl, sil, dil появились только у x86-64 в длинном режиме, а bph, sih, dih до сих пор не придумали).
                      Ответить
                      • Так можно было бы ещё и числа по регисрам сдвигать:
                        PUSHA
                        PUSH (какой там? AX?)
                        ;INC SP если надо сдвинуть на байт
                        POPA
                        но SP тоже сдвигается, а в старом стеке остаётся число :(
                        Ответить
                        • Да, жаль, что PUSHA и POPA захватывают SP, а то бы легко было эмулировать SIMD.

                          А так придётся SP где-то ещё сохранять, а потом восстанавливать.
                          Ответить
                    • Циклический сдвиг 32-битного числа на 8:
                      #include <stdio.h>
                      #include <stdint.h>
                      char * __attribute__((section(".text"))) vrot8 = "QXPPDXDDD\xc3";
                      int main() {
                          uint32_t __attribute__((fastcall)) (* rotator)(uint32_t) = (void *)vrot8;
                          uint32_t x = 0x12345678;
                          printf("rot(%x) = %x\n", x, rotator(x));
                          return 0;
                      }


                      64-битный вариант (почему-то не работает):
                      #include <stdio.h>
                      #include <stdint.h>
                      char *__attribute__((section(".text"))) vrot8 = "WXPPDXDDDDDDD\xc3";
                      int main() {
                      	uint64_t __attribute__((sysv_abi)) (* rotator)(uint64_t) = (void *)vrot8;
                      	uint64_t x = 0x123456789abcdef;
                      	printf("rot(%lx) = %lx\n", x, rotator(x));
                      	return 0;
                      }
                      Ответить
                      • В 64-битном режиме почему-то после INC RSP неправильно работает POP. Похоже, что POP в длинном режиме выравнивает значение RSP перед тем, как взять данные.
                        Ответить
                        • Например, сдвиг на 16 бит без применения INC/DEC работает:
                          #include <stdio.h>
                          #include <stdint.h>
                          const char __attribute__((section(".text"))) vrot8[] = "WXfPPfXX\xc3";
                          int main() {
                          	uint64_t __attribute__((sysv_abi)) (*rotator)(uint64_t) = vrot8;
                          	uint64_t x = 0x123456789abcdef;
                          	printf("rot(%lx) = %lx\n", x, rotator(x));
                          	return 0;
                          }


                          https://ideone.com/jycvRV
                          Ответить
                        • > почему-то
                          А ты декомпильни этот код. 'D' - не инкремент. Это префикс REX.R
                          Ответить
                          • Вот я идиот! Даже не глянул сетку опкодов. Я думал, что все новые инструкции где-то в пространстве 0F, а опкоды для старых инструкций совпадают, как было при переходе с 16-битного кода на 32-битный. А они, оказывается, часть старых опкодов затёрли новыми инструкциями.

                            Какой багор )))

                            Надо запомнить, что у x86-64 однобайтовых инкрементов/декрементов нет, на их месте ненужные префиксы Рэкс-фас; PUSHA, POPA, BOUND отменили; на месте ARPL теперь MOVSXD; AAA, AAS, DAA, DAS, AAM, AAD отменили; префиксы сегментов отменили, за исключением FS, GS; пуш и поп для сегментных регистров тоже отменили; LES, LDS, CALL FAR, JMP FAR тоже отменили.

                            И зачем-то отменили опкод 82H, который никому не мешал.

                            Как вообще можно что-то делать без однобайтовых инкрементов/декрементов???
                            Ответить
                            • Отредактировал комментарий, потому что ошибся.

                              83H –— это операции с 16/32-битным регистром, у которых в качестве второго аргумента выступало 8-битное непосредственное, которое на лету дополнялось нулями до 16/32-битного. Было очень удобно, потому что не нужно было хранить в программе лишние нули.

                              А 82H –— это просто копия 80H.
                              Ответить
                            • Идея: вместо инкремента вычитать минус единицу. Минус единицу можно представить как 0x5555555555555555 + 0x5555555555555555 + 0x5555555555555555.
                              Ответить
                              • Всё, высрал инкремент:
                                #include <stdio.h>
                                #include <stdint.h>
                                char *__attribute__((section(".text"))) incr = "fhUUfhUUfhUUfhUUTZH+:H+:H+:ZWX\xc3";
                                int main() {
                                    uint64_t __attribute__((sysv_abi)) (* increment)(uint64_t) = (void *)incr;
                                    uint64_t x = 0x123456789abcdef;
                                    printf("inc(%lx) = %lx\n", x, increment(x));
                                    return 0;
                                }


                                https://ideone.com/2Z5MCE

                                Узнал две особенности x86-64:
                                1. У кучи инструкций непосредственный аргумент может быть максимум 32-битным. Старшая половина добивается нулями. Мне пришлось делать четыре 16-битных пуша, чтобы запушить 64-битное число.
                                2. У инструкций с mod-R/M нужно указывать префикс REX.W (H), иначе результат обрежется до 32-битного.

                                Как всё сложно...
                                Ответить
                                • А вот циклический сдвиг 64-битного числа на 8:
                                  https://ideone.com/6T9Ima
                                  Ответить
                          • Кстати, почему в длинном режиме непосредственный аргумент не может быть 64-битным? Они ввели это ограничение, чтобы инструкция не получалась слишком длинной и чтобы с лёгкостью укладывалась в кэш/конвейер/предиктор?
                            Ответить
                            • > The x86 instruction set (16, 32 or 64 bit, all variants/modes) guarantees / requires that instructions are at most 15 bytes.
                              https://stackoverflow.com/questions/14698350/x86-64-asm-maximum-bytes-for-an-instruction

                              «mov qword [rax + rcx + 0x11223344], 0x55667788», например, занимает 12 байт, а с 64-битным аргументом получится 16 (и ещё всякие префиксы могут быть же). Ну и как пишут на SO — 64-битный аргумент можно напрямую загружать в регистр.
                              Ответить
                              • Поглядел, нашёл единственную инструкцию с 64-битным непосредственным: MOV reg, imm (КОП B8H+номер регистра).
                                Ответить
                              • Получается, что если я искусственно соберу длинную инструкцию (нафигачив префиксов сегмента и REP), то программа выполнится на 8086/8088, но упадёт на более современных процессорах (начиная с 80286) даже в реальном режиме?
                                Ответить
                      • Замечание про атрибуты.

                        В 64-битном режиме у «gcc» есть два соглашения о вызове: «sysv_abi» и «ms_abi». При «sysv_abi» первые 6 аргументов передаются через регистры (RDI, RSI, RDX, RCX, R8, R9), при «ms_abi» только четыре первых аргумента передаются через регистры (RCX, RDX, R8, R9). Плавающий питух передаётся через питушиный стек (до четырёх аргументов в случае «ms_abi» и до восьми аргументов в случае «sysv_abi»). Стек в обоих случаях чистит вызываемая функция. Нельзя портить RBX и RBP (а для «ms_abi» ещё и RSI и RDI) и четыре последних нумерных.

                        В 32-битном режиме вариантов способа вызова гораздо больше. «Fastcall» у разных компиляторов реализован по-разному («gcc» первые два аргумента передаёт через ecx и edx).

                        Мне больше всего понравилось, как сделали в «Watcom C»: там с помощью #pragma можно создать своё собственное соглашение, указав, через какие именно регистры нужно передавать аргументы и кто будет чистить стек.
                        Ответить
                        • Нашёл ещё одно отличие ms_abi и sysv_abi в 64-битном режиме: перед вызовом функции большого количества аргументов в ms_abi указатель на вершину стека уменьшается, как будто первые четыре аргумента тоже запушили, хотя реально в этом участке стека лежит мусор.
                          Ответить
                          • https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=vs-2017
                            The x64 Application Binary Interface (ABI) uses a four-register fast-call calling convention by default.
                            Space is allocated on the call stack as a shadow store for callees to save those registers.
                            Ответить
                          • В sysv_abi есть ещё одно страшное отличие - red zone. Кусок стека, который можно невозбранно юзать без декремента rsp.

                            Эта хуйня порождает очень весёлые баги в ring 0 (если пишешь своё ядро, например)... Её, конечно, можно отключить. Но для этого про неё надо знать.
                            Ответить
                            • Какие баги? Кто-то не учел редзону и засрал переменные?
                              Ответить
                              • > кто-то
                                Да тупо процессор во время обработки прерывания. Он же ничего не знает про эти ваши редзоны.
                                Ответить
                                • Я тоже ничего не знаю про «редзоны». Именно поэтому я за «PHP».
                                  Ответить
                            • Можно пример висёлого ьуги-вуги?
                              Ответить
                              • И мне можно? Я тоже люблю буги-вуги.

                                Я надеваю штиблеты и галстук-шнурок,
                                Я запираю свою дверь на висячий замок.
                                Ответить
                        • > вызываемая
                          Вызывающая.
                          Ответить
                          • Да, посмотрел дизасм, теперь уже понял, что вызывающая, как в cdecl.

                            То есть можно создавать функции с переменным количеством аргументов, а вот сделать принудительный пуш/поп посредством каламбура типов не получится.
                            Ответить
                            • Напиши себе ascii'шные push и pop. Они то гарантированно будут работать в отличие от игры с конвеншенами. Вот только оптимизатор потом обидится и достанет локальные переменные не оттуда...
                              Ответить
                            • При очистке вызываемым тоже можно замутить переменное количество аргументов. Передаёшь ему количество в регистре или крайнем аргументе да и всё.

                              В общем-то это даже надёжней т.к. вызываемый видит обе границы аргументов. А в cdecl - только нижнюю.
                              Ответить
                              • Как я тебе стек из вызываемой функции чистить буду? RET ожидает кококоличество очищаемых байтиков в непосредственном аргументе.

                                Варианты реализации:
                                1. Патчить код (изменять байтик, следующий за опкодом RET). Для этого нужно снимать защиту.

                                2. Свитч-кейс, в каждой ветке которого будет RET N с соответствующим значением N.

                                3. Извлекать из стека адрес возврата, чистить стек, после этого делать джамп (ну или push+ret) на сохранённый в регистре адрес возврата.
                                Ответить
                                • 3.

                                  А можно даже количество не передавать. Не дочитал все vararg'и - получай UB.
                                  Ответить
                                  • З.Ы. В общем-то вызов printf() с кривой строкой формата - это уже UB. Так что такая реализация имеет право на жизнь.
                                    Ответить
                                • > RET N
                                  Кстати, а ведь ret и ret N спекулятивно возвращаются к вызывающему независимо от N и состояния стека? Т.е. вариант с push + ret будет более правильным, нежели jmp...
                                  Ответить
                                  • В варианте с jmp предиктор будет думать, что подпрограмма никогда не кончится.
                                    Ответить
                                  • В жопу спекулятивно.
                                    Ответить
                                    • > в жопу спекулятивно
                                      Сперва присунул, потом спросил?
                                      Ответить
                                      • Всегда так делаю.
                                        Ответить
                                      • Нет, сначала присунул, потом сказал "no homo" и тем самым все сайд-эффекты обнулились. Ну или почти все.
                                        Ответить
                                        • Мне кажется, спекулятивно –— это наоборот: пригрозить присунуть, но не присунуть. Или присунуть палец...
                                          Ответить
                                        • или не обнулились, если речь о мельтдауне
                                          Ответить
                                        • > и тем самым все сайд-эффекты обнулились. Ну или почти все.
                                          …кроме ЗППП.
                                          Ответить
                                • В жопу стек.
                                  Ответить
    • Устроился в Яндекс. Точнее в Яндекс.Еда. Пока курьером.
      Ответить
      • Задачки, которые нахуй не пригодятся в работе, задавали?
        Ответить
        • Дали три адреса и просили найти кротчайший путь между всеми тремя чтобы быстрее принести пельмени

          Я использовал Яндекс.Карты
          Ответить
    • Заметил, что активность с 21:00 до 01:00 на Говнокоде заметно пропадает.
      С чем это связано?
      Ответить
      • я дрочу в это время
        Ответить
        • Петух явно чём-то занят. Основная активность то от него.
          Ответить
          • Петухи рано садятся спать. Им рано вставать.
            Ответить
      • Это по какому времени?
        Ответить
        • По дефолтному
          Ответить
          • Мне тут не спалось, и я решил поболтать с 'J', так вот она говорит что активность падать примероно с 1:00 до 8:00 по UTC+0:
            load 'web/gethttp'
               load 'convert/pjson'
               coinsert 'pjson'
            
               getcomments =: verb define
                   comments =. 0 $ a:
                   i =. 0
                   args =. ''
                   while. i < y do.
                       comments =. comments , dec gethttp 'http://b.gcode.cx/ngk/api/comments' , args
                       args =. '?before=' , (_1 ; _3 1) {:: comments
                       i =. i + 20
                   end.
                   comments
               )
               
               comments =: getcomments 400
               hours =: /:~ ("[email protected]:{~&11 [email protected]{::~&_3 1)every comments
               ([email protected]] ,. +/"[email protected][email protected]])hours
            0 20
             1  7
             3  1
             4  3
             5  4
             6  1
             7  2
             8 10
             9 26
            10 17
            11 14
            12 12
            13 41
            14 38
            15 44
            16 13
            17 29
            18 32
            19 15
            20 15
            21 18
            22 25
            23 13
            Вобщет по хорошему надо было считать среднее за каждый час в день, но мои красны глазы жаждут погрузится во тьму.
            Ответить

    Добавить комментарий