LINUX.ORG.RU

Ассемблерная вставка в код C++

 , ,


0

1

Здравствуйте, есть задача: нужно для 16 битной переменной члена класса, написать встроенную функцию которая бы выводила младший байт переменной, и функцию которая бы выводила ее старший байт, на ассемблере. Типо: Class Foo { int A public: ... int Alow()//возвращает младший байт { asm(.?.); } int Ahigh()//Старший байт { asm(...); } }; Если бы я писал такое под DOS или Windows, проблем бы не возникло, но под Linux я не знаю как. Спасибо за помощь.

Интересует вопрос, почему именно на ассемблере?

Kiborg ★★★ ()

А на кой хрен тут ассемблер? & и >> во все поля

anonymous ()

Если бы я писал такое под DOS или Windows, проблем бы не возникло

Т.е. ты считаешь, что вставка ассемблера (языка a) в код C++ (язык b), для выполнения операции, которая может быть выполнена в языке b стандартными функциями, это не проблема? Ох уж эти виндовс-программисты.

comp00 ★★★★ ()
Последнее исправление: comp00 (всего исправлений: 1)

Если бы я писал такое под DOS или Windows, проблем бы не возникло, но под Linux я не знаю как.

Ассемблер же вроде машинными инструкциями оперирует, не? Или под венду гуглится, а с «в линукс» нет?

Тут скорее по компиляторам гуглить надо.

Первая ссылка

ziemin ★★ ()
#include <stdio.h>
#include <stdint.h>
typedef union
{  
	uint16_t  Word;
	struct  bytes
	{
		uint8_t  HiByte;
		uint8_t  LoByte; 
	} b; 
} twobytes;

int main(int argc, char* argv[]) {
	uint16_t member = 0x0102;
	twobytes *x = (twobytes*) & member;
	x->Word = member;
	printf("%x %x, %x\n", member, x->b.HiByte, x->b.LoByte);
        
        return 0;
}

Либо:

#define HI_UINT16(a) (((a) >> 8) & 0xFF)
#define LO_UINT16(a) ((a) & 0xFF)

В случае необходимости оценивать эфективность:

gcc -S main.c
и тупить в полученный код в main.s

Писать на ассемблере, когда можно написать тот же (или тот же по скорости выполнения) код на C это грех.

ihanick ()
Ответ на: комментарий от stevejobs

смертельный номер

в смысле что если выравнивание struct {int16_t} будет не такое как выравнивание int16_t ?

ihanick ()
Ответ на: комментарий от ihanick

просто представил, как будет выглядеть код, состоящий сплошь из таких кастов. Как в процессе отладки кто-нибудь сорвется и расстреляет из магнума весь офис

а где оно будет не такое?

stevejobs ★★★☆☆ ()
Ответ на: комментарий от ihanick

union { ... } twobytes - за такое расстреливать надо. На little-endian машинах, кстати, работать не будет.

anonymous ()
Ответ на: комментарий от Kiborg

Такова задача. Это условие надо выполнить, на C сам бы справился.

WaldeMar1321 ()
Ответ на: комментарий от stevejobs

просто представил, как будет выглядеть код, состоящий сплошь из таких кастов

ну, у меня есть ещё несколько вариантов, может какой-нидь лучше? Для больших индейцев нужен дефайн, варианты практически идентичные, сдвиги и «И» на x86 интересней выглядят, на arm/HCS12 оно в виде сдвигов и «И», т.е. может быть медленнее чем вариант считать байт из памяти.

Для asm вставки код немного жирнее, т.к. есть сначала сохранение в стековые переменные, а потом сохранение в стек для printf.

#include <stdio.h>
#include <stdint.h>
typedef union
{  
	uint16_t  Word;
	struct  bytes
	{
		uint8_t  LoByte; 
		uint8_t  HiByte;
	} b; 
} twobytes;


inline static uint8_t get_high(uint16_t& word) {
	return reinterpret_cast<uint8_t*>(&word)[1];
}

inline static uint8_t get_low(uint16_t& word) {
	return reinterpret_cast<uint8_t*>(&word)[0];
}

inline static uint8_t get_high_arithm(uint16_t& word) {
	return (((word) >> 8) & 0xFF);
}

inline static uint8_t get_low_arithm(uint16_t& word) {
	return ((word) & 0xFF);
}


#define HI_UINT16(a) (((a) >> 8) & 0xFF)
#define LO_UINT16(a) ((a) & 0xFF)

#define LOWHIGH(word, tmp_high,tmp_low) \
	asm( \
			"movw %2, %%ax\n\t" \
			"movb %%ah, %0\n\t" \
			"movb %%al, %1\n\t" \
			: "=m"(tmp_high), "=m"(tmp_low) \
			: "m"(word) \
			: "ax" \
	   )

int main(int argc, char* argv[]) {
	uint16_t member = 0x0102;
	twobytes *x = (twobytes*) & member;
	printf("marker\n");
	printf("%x, %x\n", ((twobytes*)&member)->b.HiByte, ((twobytes*)&member)->b.LoByte);
	printf("%x, %x\n", get_high(member), get_low(member));
	printf("%x, %x\n", reinterpret_cast<uint8_t*>(&member)[1], reinterpret_cast<uint8_t*>(&member)[0]);
	printf("%x, %x\n", HI_UINT16(member), LO_UINT16(member));
	printf("%x, %x\n", get_high_arithm(member), get_low_arithm(member));

	uint8_t tmp_high;
	uint8_t tmp_low;

	LOWHIGH(member, tmp_high, tmp_low);
	printf("%x, %x\n", tmp_high, tmp_low);
        return 0;
}
ihanick ()
Ответ на: комментарий от ihanick

twobytes *x = (twobytes*) & member;

twobytes x; x.Word = member? И typedef для union не нужен если это C++.

reinterpret_cast<uint8_t*>(&word)[0]

По идее — strict aliasing violation.

#include <cstdio>
#include <cstdint>

#include <endian.h>

template <typename T, typename B, size_t bytes>
union UnionCast {

  private:

    T b;
    B bs[bytes];

  public:

    inline UnionCast(T b_) : b(b_) {}

    inline B operator[](size_t n) {
#if __BYTE_ORDER == __LITTLE_ENDIAN
        return bs[n];
#elif __BYTE_ORDER == __BIG_ENDIAN
        return bs[bytes - n - 1];
#else
#error
#endif
    }

};

typedef UnionCast<uint16_t, uint8_t, 2> B16;
static inline uint8_t low(B16 x) { return x[0]; }
static inline uint8_t high(B16 x) { return x[1]; }

void f(B16 x)
{
    // movzbl	%dil, %edx
    // movzbl	%ah, %ecx
    printf("%x %x\n", low(x), high(x));
}

void g(uint16_t x)
{
    // movzbl	%dh, %ecx
    // movzbl	%dil, %edx
    printf("%x %x\n", x & 0xFF, (x >> 8) & 0xFF);
}

int main()
{
    f(0xabcd);
    g(0xabcd);
}

Но это gcc extension (http://gcc.gnu.org/bugs/#nonbugs) так как

9.5 Unions

In a union, at most one of the non-static data members can be active at any time, that is, the value of at most one of the non-static data members can be stored in a union at any time.

так что обычные маски и сдвиги и проще и лучше.

quasimoto ★★★★ ()
Последнее исправление: quasimoto (всего исправлений: 1)
Ответ на: комментарий от quasimoto

так что обычные маски и сдвиги и проще и лучше.

кст на arm у gcc в -O и выше оба варианта одинаковые и сделаны через сдвиги.

ihanick ()
Ответ на: комментарий от ihanick

Товарищ, такой вопрос к тебе отвлеченный, а есть ли возможность эти 16 бит разбить на 4 группы бит и вытянуть их произвольным способом? Примерно тем же способом на С. З.Ы. У меня удручающе малый опыт работы на низком уровне(((

WaldeMar1321 ()
Ответ на: комментарий от WaldeMar1321

Логика такая же: сдвинуть и наложить маску или накладывать разные маски и сдвигать.

Как всегда код на asm немного менее оптимален из-за обвязки, в остальном идентичен -S

#include <stdio.h>
#include <stdint.h>

#define GET4BITS(a,num) (((a) >> num) & 0xF)

#define GET4BITS_X86(res,word,num)  \
    asm( \
            "movw %1, %%ax\n\t" \
            "shrw %2, %%ax\n\t" \
            "andb $15, %%al\n\t" \
            "movb %%al, %0\n\t" \
            : "=m"(res) \
            : "m"(word), "I"(num) \
            : "ax" \
       )

int main(int argc, char* argv[]) {
    uint16_t member = 0x1234;
    printf("%x %x %x %x\n",
            GET4BITS(member, 12),
            GET4BITS(member, 8),
            GET4BITS(member, 4),
            GET4BITS(member, 0)
          );
    uint8_t res12,res8,res4,res0;
    GET4BITS_X86(res12,member, 12);
    GET4BITS_X86(res8,member, 8);
    GET4BITS_X86(res4,member, 4);
    GET4BITS_X86(res0,member, 0);

    printf("%x %x %x %x\n", res12,res8,res4,res0);
    return 0;
}

ihanick ()
Ответ на: комментарий от ihanick

Огромное спасибо! И всем тоже. Надеюсь когда-то тоже самоучкам(и не только) помогать смогу.))

WaldeMar1321 ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.