пятница, 13 марта 2009 г.

Цвета в текстовом режиме

Программа создает таблицу цветов в текстовом режиме. Комприлировать fasm-ом

org 100h
        mov     ax, 3
        int     10h         ; установка видеорежима

        mov     ax, 1003h
        mov     bl, 0       ; 0 - яркие цвета, 1 - мигание
        int     10h


        push    0b800h
        pop     es          ; es = сегмент видеопамяти
        mov     di, 0       ; di = начало сегмента

        cld                 ; прямое направление для строковых операций

        mov     bx, hex_numbers  ; устанавливаем таблицу для команды xlat
        mov     ah, 0            ; ah будет хранить текущий атрибут

next_color:
        mov     al, ' '
        stosw               ; выводим пробел

        mov     al, ah
        shr     al, 4;      ; в al остаются старшие 4 бита
        xlatb               ; преобразуем их в шестнадцатиричную цифру
        stosw               ; выводим первую цифру

        mov     al, ah
        and     al, 0xf;    ; в al младшие 4 бита
        xlatb               ; преобразуем их в шестнадцатиричную цифру
        stosw               ; выводим вторую цифру

        mov     al, ' '
        stosw               ; выводим пробел

        inc     ah          ; следующий атрибут

        test    ah, 0xf     ; если вывели 16 атрибутов
        jnz     @f          ; заполняем строку до конца
        dec     ah          ; пробелами
        mov     cx, 80 - (16 * 4) ; сколько символов заполнять
        rep     stosw
        inc     ah

@@:
        test    ah, ah      ; если ah=0, то перебрали все атрибуты
        jnz     next_color  ; повторяем для всех возможных атрибутов

        mov     ah, 0       ; ждем клавишу
        int     16h

        ret                 ; выходим

hex_numbers db '0123456789ABCDEF'  ; шестнадцатиричные цифры

четверг, 12 марта 2009 г.

C++, полиморфизм

Маленький пример с виртуальными функциями.

#include <iostream>

using namespace std;

class A
{
public:
    void f()
    {
        cout<<"a"<<endl;
    }

    void v()
    {
        cout<<"A"<<endl;
    }
};

class B: public A
{
public:
    void f()
    {
        cout<<"b"<<endl;
    }

    virtual void v()
    {
        cout<<"B"<<endl;
    }
};

class C: public B
{
public:
    void f()
    {
        cout<<"c"<<endl;
    }

    void v()
    {
        cout<<"C"<<endl;
    }
};

void u(A& a)
{
    a.f();
    a.v();
}

void v(A* a)
{
    a->f();
    a->v();
}

void x(B& b)
{
    b.f();
    b.v();
}

void y(B* b)
{
    b->f();
    b->v();
}

void z(B b)
{
    b.f();
    b.v();
}

int main(int argc, char* argv[])
{
    B b;
    C c;
    
    u(b);  u(c);  // aA aA
    v(&b); v(&c); // aA aA

    x(b);  x(c);  // bB bC
    y(&b); y(&c); // bB bC
    
    z(b);  z(c);  // bB bB

    return 0;
}

вторник, 10 марта 2009 г.

Представление отрицательных чисел в дополнительном коде

Во всех примерах будут использоваться 8-разрядные числа.

Чтобы получить представление отрицательного числа в дополнительном коде, надо:

  1. Взять представление этого числа со знаком +
  2. Инвертировать все цифровые разряды
  3. Прибавить 1 в младший разряд

Пример: найдем представление числа -108 в дополнительном коде.

1. Запишем число 108 в двоичном представлении:

01101100

2. Инвертируем все разряды:

10010011

3. Прибавляем 1 в младший разряд:

 10010011
+00000001
---------
 10010100

Число -108 в дополнительном коде будет выглядеть так: 10010100

Правило перевода можно упростить:

  1. Взять представление числа со знаком +
  2. Инвертировать все цифровые разряды до младшей единицы. Младшая единица и следующие за ней нули остаются неизменными

В самом деле, это легко заметить, если рассмотреть представления чисел 108 и -108:

 108 = 01101100ДК
-108 = 10010100ДК

Некоторые 8-битные числа в дополнительном коде:

Число ДК
-128 1000 0000
-127 1000 0001
-126 1000 0010
-125 1000 0011
... ...
-3 1111 1101
-2 1111 1110
-1 1111 1111
0 0000 0000
1 0000 0001
2 0000 0010
3 0000 0011
... ...
125 0111 1101
126 0111 1110
127 0111 1111

x86: Условные переходы

КомандаJump if ...Условие при сравнении
cmp x, y
Условие перехода
(состояние флагов)
ja
jnbe
Above
Not Below or Equal
x > yCF = 0 and ZF = 0
jae
jnb
jnc
Above or Equal
Not Below
Not Carry
x ≥ yCF = 0
jb
jnae
jc
Below
Not Above or Equal
Carry
x < yCF = 1
jbe
jna
Below or Equal
Not Above
x ≤ yCF = 1 or ZF = 1
je
jz
Equal
Zero
x = yZF = 1
jne
jnz
Not Equal
Not Zero
x != yZF = 0
jg
jnle
Greater
Not Less or Equal
x > y
(знаки учитываются)
SF = OF and ZF = 0
jge
jnl
Greater or Equal
Not Less
x ≥ y
(знаки учитываются)
SF = OF
jl
jnge
Less
Not Greater or Equal
x < y
(yзнаки учитываются)
SF != OF
jle
jng
Less or Equal
Not Greater
x ≤ y
(знаки учитываются)
SF != OF or ZF = 1
jsSignedSF = 1
jnsNot SignedSF = 0
joOverflowOF = 1
jnoNot OverflowOF = 0
jp
jpe
Parity
Parity Even
PF = 1
jnp
jpo
Not Parity
Parity Odd
PF = 0
jcxzCX ZeroCX = 0

Многие условные переходы имеют по несколько имен. Например, ja (больше) и jnbe (не меньше и не равно) обозначают одно и то же. je и jz также являются одинаковыми командами. Обычно если, сравнивают два аргумента, используют je, чтобы показать, что переход произойдет, если аргументы равны. Если сравнивают аргумент с нулем, тогда используют jz.

понедельник, 9 марта 2009 г.

ASCII table in asm

Программа рисует таблицу ASCII. Для компиляции используется flat assembler. Таблица составляется с помощью макросов на этапе компиляции. Во время выполнения таблица просто копируется в видеопамять.

org 100h

        mov     ax, 3
        int     10h         ; установка видеорежима

        push    0b800h
        pop     es          ; es = сегмент видеопамяти

        mov     si, table   ; si = таблица, откуда читать данные
        mov     di, 0       ; di = куда писать

        cld                 ; прямое направление для строковых операций
        mov     cx, 18*80   ; 18 строк по 80 символов
        mov     ah, 0Fh     ; атрибут

next_char:
        lodsb               ; считываем символ
        stosw               ; записываем в видеопамять вместе с атрибутом
        loop    next_char

        mov     ah, 0       ; ждем клавишу
        int     16h
        ret                 ; выходим

; символы, используемые для построения таблицы:
VERT_LINE  = 0b3h
HORIZ_LINE = 0c4h
CROSS      = 0c5h

table:

; шапка таблицы:
db 3 dup(' ')
db VERT_LINE, " 0 1 2 3 4 5 6 7 "
db VERT_LINE, " 8 9 A B C D E F "
FIIL_LEN = 80 - ($-table)    ; FIIL_LEN = сколько пробелов надо добавить
db FIIL_LEN dup (' ')        ; дополняем строку да 80 символов пробелами

db  3 dup (HORIZ_LINE), CROSS
db 17 dup (HORIZ_LINE), CROSS
db 17 dup (HORIZ_LINE)
db FIIL_LEN dup (' ')

; содержимое таблицы:
y=0             ; y = строка
c=0             ; c = код символа
while y<16
   ; цифра слева от таблицы
   if y<9
      db ' ', y+'0', ' '  ; цифры 0, 1, ..., 9
   else
      db ' ', y+'0'+7,' ' ; шестнадцатиричные A, B, C, ..., F
   end if

   ; две группы по 8 символов, разделенные вертикальной чертой
   repeat 2
      db VERT_LINE, ' '
      repeat 8
         db c, ' '
         c=c+1
      end repeat
   end repeat
   db FIIL_LEN dup (' ')   ; дополняем строку да 80 символов пробелами
   y=y+1
end while

суббота, 7 марта 2009 г.

x86: idiv, sar

Целые числа можно легко делить на степени двойки используя команду sar. Однако команда sar может дать результат отличный от полученного с помощью команды idiv.

idiv округляет результат в сторону нуля. Знак остатка всегда равен знаку делимого.

17 idiv (-8) = -2 (1 в остатке)
17 = (-8) * (-2) + 1

-17 idiv 8 = -2 (-1 в остатке)
-17 = 8 * (-2) + (-1)

В вычислениях с sar, результат округляется в меньшую сторону (в сторону минус бесконечности)

-17 sar 3 = -3