// смещение содержимого экрана по горизонтали и вертикали

#include "sysgraph.h"
#include <stdio.h>
#include <stdlib.h>
#include <graph.h>
#include "sysp.h"
#include <dos.h>

void HorScroll(unsigned, unsigned);
void VerScroll(unsigned);
void SetColumn(unsigned char);

void main(void) {

     struct videoconfig vc;
	  unsigned char   i,j;

     _setvideomode(_DEFAULTMODE);

     // устанавливаем логическую ширину экрана в 100 символов

	  SetColumn(100);

	  for(i = 0; i < 24; i++)
			 printf("\n text text text text text text text text text text text text text text text text ");

     // производим горизонтальное смещение экрана

	  for(i = 0; i < 160; i++)
          HorScroll(i,8);

	  getch();

     // производим вертикальное смещение экрана

	  for(i = 0; i < 200; i++)
			 VerScroll(i);

     getch();

	  _setvideomode(_DEFAULTMODE);
}


/**
*.Name    HorScroll
*
*.Title   Горизонтальный сдвиг содержимого экрана.
*
*.Descr   Функция смещает содержимое экрана влево на определенное
*         число пикселов.
*
*.Proto   void HorScroll(unsigned offset, unsigned wide)
*
*.Params  offset - величина смещения в пикслах,
*
*         wide   - ширна символов (8 или 9).
*
*.Return  Не используетя.
*
*.Sample  scroll.c
**/
void HorScroll(unsigned offset, unsigned wide) {

     unsigned start_addr_reg, panning_reg;
     div_t res;

	  res = div(offset,wide);

     start_addr_reg = res.quot;
     panning_reg = (wide == 9) ?
						 ((res.rem == 0) ? 8 : res.rem - 1) : res.rem;

	  _asm {

			 push ds

; вычисляем адрес регистра состояния 1 (3BAh/3DAh)

          xor  ax,ax
          mov  es,ax
          mov  dx,es:[463h]
          add  dx,6

; ожидаем начало обратного вертикального хода луча

          in   al,dx
          nop
          nop
          test al,08h
          jz   wait_on

     wait_off:

          in   al,dx
          nop
          nop
          test al,08h
          jnz  wait_off

     wait_on:

          in   al,dx
          nop
          nop
          test al,08h
          jz   wait_on

; вычисляем адрес порта индексного регистра контроллера ЭЛТ

          sub  dx,6

; выбираем для доступа младший байт регистра начального адреса

          mov  al,0Dh
          out  dx,al

; вычисляем адрес порта регистра данных контроллера ЭЛТ

          inc  dx

; устанавливаем новый начальный адрес

          mov  ax,start_addr_reg
          out  dx,al

; снова получаем адрес порта регистра состояния 1

          add  dx,5

; сбрасываем внутренний тригер контроллера атрибутов, управляющий
; адресацией его регистров

          in   al,dx
          nop
          nop

; выбираем регистр горизонтального панорамирования

          mov  dx,3C0h
			 mov  al,33h
          out  dx,al

          mov  ax,panning_reg
          out  dx,al

          pop  ds
     }
}



/**
*.Name    VerScroll
*
*.Title   Вертикальный сдвиг содержимого экрана.
*
*.Descr   Функция смещает содержимое экрана вверх на определенное
*         число пикселов.
*
*.Proto   void VerScroll(unsigned offset)
*
*.Params  offset - величина смещения в пикслах.
*
*.Return  Не используетя.
*
*.Sample  scroll.c
**/
void VerScroll(unsigned offset) {

     unsigned start_addr_reg, preset_row_reg;
     div_t res;
     BIOS_VAR _far  *bios_var_ptr;


     bios_var_ptr = (BIOS_VAR _far *) FP_MAKE(0x0000, 0x0410);

	  res = div(offset,bios_var_ptr -> char_height);

     start_addr_reg = res.quot * (bios_var_ptr -> columns);
     preset_row_reg = res.rem;


     _asm {
			 push ds

; вычисляем адрес регистра состояния 1 (3BAh/3DAh)

          xor  ax,ax
          mov  es,ax
          mov  dx,es:[463h]
          add  dx,6

; ожидаем начало обратного вертикального хода луча

          in   al,dx
          nop
          nop
          test al,08h
          jz   wait_on

     wait_off:

          in   al,dx
          nop
          nop
          test al,08h
          jnz  wait_off

     wait_on:

          in   al,dx
          nop
          nop
          test al,08h
          jz   wait_on

; вычисляем адрес порта индексного регистра контроллера ЭЛТ

          sub  dx,6

; модифицируем младший байт регистра начального адреса

          mov  al,0Dh
          out  dx,al
          inc  dx
          mov  ax,start_addr_reg
          out  dx,al

          dec  dx

; модифицируем старший байт регистра начального адреса

          mov  al,0Ch
          out  dx,al
          inc  dx
          mov  al,ah
          out  dx,al

; вычисляем адрес порта регистра состояния 1

          add  dx,5

; ожидаем вертикальный обратный ход луча

wait_next:

          in   al,dx
          nop
          nop
          test al,08h
          jz   wait_next

; определяем адрес порта индексного регистра контроллера ЭЛТ

          sub  dx,6

; устанавливаем регистр предустановки линии скаанирования

          mov  al,8
          out  dx,al
          mov  ax,preset_row_reg
          inc  dx
          out  dx,al

          pop  ds
     }
}


/**
*.Name    SetColumn
*
*.Title   Установка логическо ширины экрана.
*
*.Descr   Функция устанавливает логическую ширину экрана.
*
*.Proto   void SetColumn(unsigned char col)
*
*.Params  col - логическая ширина экрана (в символах).
*
*.Return  Не используется.
*
*.Sample  scroll.c
**/
void SetColumn(unsigned char col) {

     _asm {
          xor  ax,ax
          mov  es,ax

; получаем адрес порта индексного регистра контроллера ЭЛТ (3B4h/3D4h)

          mov  dx,es:[463h]

; выбираем для обмена регистр логической ширины экрана

          mov  al,13h
          out  dx,al

; вычисляем адрес порта регистра данных контроллера ЭЛТ (3B5h/3D5h)
          inc  dx


; модифицируем переменную BIOS, содержащую число символов в строке

          mov  al,col
          mov  es:[44Ah],al

; регистр логической ширины экрана в текстовых режимах содержит
; смещение между соседними строками текста в словах

          shr  al,1
          out  dx,al
     }
}
