Great2! – código fonte e técnicas utilizadas

Em 1994 eu era o SysOp da WarmBoot BBS e cursava o último ano de Ciência da Computação. Durante muito tempo, os BBS reinaram absolutos conectando pessoas em todo o mundo, antes de serem eliminados da face da terra com a chegada da Internet para o público geral.

Além da troca de mensagens, os BBS serviam também como repositórios de arquivos, em geral sharewares, freewares, imagens, etc. Usuários discavam para o BBS para baixar/subir arquivos, trocar mensagens, etc. Havia uma tradição em que cada BBS adicionava um pequeno arquivo texto aos arquivos compactados (.arj, .zip, .lzh, etc) do acervo, indicando que ele foi baixado daquele determinado BBS, além de informações básicas como: o nome e telefone do BBS, parâmetros para conexão via modem, etc. Enfim, uma propaganda que ajudava a divulgar a existência do BBS para outras pessoas e, com sorte, captar novos usuários pagantes. Esses arquivos precisavam ser minúsculos, afinal, estamos falando de conexões discadas que variavam entre 2.400bps e 56Kbps, dependendo do seu modem e da qualidade da linha telefônica. Pra se ter ideia, um arquivo de 100KB demoraria entre 5 e 8 minutos para ser baixado em uma conexão de 2.400bps, dependendo das condições da linha!

O WarmBoot BBS era referência na distribuição de Demos de multimidia. Eramos distribuidores oficiais da Future Crew e da Twilight Zone. Sempre achei fantástico o que os demogroups faziam usando assembly, em uma época em que o clock da maioria dos PCs não passava de 40Mhz.

Decidi então fazer algo diferente da maioria dos BBS: ao invés de incluir um arquivo texto chato de “propaganda”, criei algo mais divertido e animado – uma “intro” em modo texto, o great2!. Como pode ser visto no vídeo abaixo (gravado em uma janela emulada do DOS), ao ser executado, o great2! fazia a “propaganda do BBS” quicar na tela, enquanto rolava uma música de fundo juntamente com um VU bargraph animado (no vídeo, a rolagem pode não parecer tão suave, afinal, foi gravado em um emulador de DOS que simulava inclusive a taxa de refresh de um monitor CRT).

Lembre-se, o Windows 95 nem mesmo tinha sido lançado e o Windows 3.1 era meramente uma interface gráfica tosca que rodava em cima do MS-DOS. A maioria dos programas ainda eram executados em modo texto (80 colunas x 25 linhas), nada de mouse, janelas etc! Aqueles mais abonados tinham placas de som em seus computadores (SoundBlaster era o suprassumo da época), monitores CRT (Syncmaster 3 era o que tinha de melhor) e placas “Trident” Super VGA com no máximo 1MB de RAM.

A parte mais interessante do great2! era com certeza o efeito da tela quicando. O modo texto, por padrão, tem um número limitado de colunas e linhas para se trabalhar (80 x 25). Normalmente, rolar um texto significava deslocar as linhas para cima ou para baixo, o que obviamente não causava uma rolagem “suave”, já que cada linha tinha 16 pixels de altura, ou seja, cada deslocamento significava mover o texto na tela no mínimo 16 pixels para cima ou para baixo. O “tchã” do great2! era usar recursos de programação gráfica VGA juntamente com o sincronismo do tempo de retrace vertical dos monitores para “pintar a tela” começando no momento certo e no “pixel” certo, causando um efeito de rolagem suave. O sincronismo do retrace era essencial – sem ele é como se partes da imagem parecessem “quebradas”.

Vou deixar a I.A. explicar o conceito, pois creio que ela fará isso de forma mais didática:

O “motor de rolagem” do programa não redesenha a tela: ele desenha o conteúdo uma vez na memória de vídeo VGA em modo texto e depois altera os registradores da [placa gráfica] VGA para mudar a linha inicial de exibição. Como a VGA permite deslocar a imagem por scanlines dentro das células de texto (25 linhas x 16 pixels = 400 scanlines no total), o movimento fica suave, mesmo sem usarmos um modo gráfico.

A sincronização é feita esperando o vertical retrace do monitor CRT, isto é, o breve intervalo entre um quadro e outro em que o feixe de raios catódicos volta ao topo da tela. Ao atualizar os registradores nesse momento, o código evita flicker e tearing, algo equivalente a usar VSync em gráficos modernos.

Em resumo: o efeito funciona como uma “câmera” que se move sobre uma tela já pronta, usando recursos do hardware VGA e sincronizando as mudanças com o refresh do monitor.

Também pedi pra ela fazer uma simulação animada, pra ficar ainda mais fácil de entender o que acontece:

Simulador de Hardware: Scroll Suave VGA (Modo Texto)

Lado Esquerdo: O monitor CRT real. Repare que o texto não se move na memória, a VGA apenas muda onde a tela começa.
Lado Direito: A VRAM (Memória) está fixa. O retângulo vermelho representa os Registradores da VGA mudando a cada ciclo de Retrace.

É claro que o impacto do great2! não ficava apenas no movimento suave, mas também no tamanho minúsculo do executável (5.7Kb) e na música de fundo. Para tocar a música, usei o HSC Player, disponibilizado publicamente por Hannes Seifert, da NEO Productions (uma desenvolvedora de jogos da Austrália). A música também não é de minha autoria! Peguei pronta na internet e confesso que não me lembro quem era o autor. O “anúncio”, ou seja, o texto colorido foi feito usando o The Draw, um editor de telas ANSI amplamente utilizado pelos SysOps para construir as telas de boas vindas e menus dos BBS. Telas ANSI eram a única forma de adicionar texto colorido na interface de um BBS. Lembre-se, tudo era texto/bytes sendo transmitido pela linha telefônica da forma mais crua possível. Usando ANSI conseguiamos adicionar cores aos caracteres. Em conexões lentas, dava pra ver as telas sendo desenhadas caractere por caractere – haja paciência!

Abaixo está o código fonte do great2!, incluindo meus comentários escritos em um inglês vergonhoso e cheio de erros (tinha 19 anos e havia aprendido inglês por conta própria, lendo revistas e livros americanos sobre programação). Muitas vezes recebi mensagens de pessoas que desacreditavam que um executável tão pequeno podia fazer “tanta coisa”.

O código mostra várias técnicas típicas da programação Assembly/DOS que hoje parecem bem “artesanais”, mas eram normais na época: acesso direto à memória de vídeo (B800h) em vez de APIs gráficas, escrita direta em portas de hardware da VGA (03DAh, 03D4h, 03C8h, 03C9h), sincronização manual com o retrace vertical do monitor, rotação de palheta de cores, uso de interrupções BIOS/DOS (int 10h, int 16h, int 21h), controle explícito de registradores, segmentos e pilha, além de CLI/STI para bloquear interrupções durante trechos críticos.

Também chama atenção o uso de rotinas externas para música AdLib, com callback de IRQ para desenhar VUs na tela enquanto a música toca, e a tela inicial armazenada de forma comprimida e descompactada diretamente na memória de vídeo. Para um programador moderno, é curioso porque quase tudo que hoje seria delegado ao sistema operacional, driver gráfico, engine ou biblioteca era feito “na unha”, conversando diretamente com o hardware e sincronizando o tempo visual quase quadro a quadro.

A AdLib era uma placa de som para PCs, famosa no fim dos anos 1980/início dos 1990, que gerava música por síntese FM usando o chip Yamaha OPL2. As Sound Blasters e demais placas de som que vieram depois mantiveram a compatibilidade básica com a AdLib.

As barras do VU, que simulavam um spectrum analyzer, representavam na verdade o nível de sinal de cada instrumento que estava tocando naquele momento, e dava um toque especial e um certo dinamismo na apresentação.

O great2! era compilado com o tasm – o Turbo Assembler, assembler da Borland usado principalmente em DOS para escrever e compilar programas em linguagem Assembly para processadores x86, como o 8086/286/386.

;*==========================================================================*
;|  WarmBoot BBS - WarmBoot Group Productions ( 07/94 ) - WarmBooter Typing |
;*==========================================================================*
;|                                                                          |
;|   This is the second BBS advertisement I write and I hope this one is    |
;| better than the previous (great!.exe) :)) I decided to release this code |
;| to PD... you will find some VGA interesting tricks here, nothing very    |
;| impressive but I think people with less experience with VGA programming  |
;| (like me :-) will enjoy it !                                             |
;|                                                                          |
;| The greetings go to :                                                    |
;|                                                                          |
;|    Dart Vader / Iguana / Future Crew / Renaissance / Twilight Zone       |
;| Triton / CodeBlasters / and all the other groups out there & IRC users ! |
;|                                                                          |
;|  ->>  Thanks to NEO Productions for the HSC-Player ! Great Work !  <<-   |
;|                                                                          |
;|  Personal greetings go to :                                              |
;|                                                                          |
;|      Dart Vader / Luiz Benfica / Logi Ragnarsson / Kevin Sartorelli      |
;|         Trug / JCAB / The people at Ciagri / Chico, Pedro, Pupo,         |
;|       Fabio, Waltinho, Marcelo, Tamora and all my friends at EEP !       |
;|                                                                          |
;|  Also I want to thanks the following musicians for make the best music   |
;|  in the world :                                                          |
;|                                                                          |
;|        Iron Maiden / Mettalica / Sepultura / AC/DC / Steve Vai           |
;|  Joe Satriani / Pantera / Vinnie Moore / Joe Tafola / Steve R. Voughan   |
;| Ozzy Osbourne / Sex Pistols / Garotos Podres / Van Halen / King Diamond  |
;|                             Blues Etilicos                               |
;|                                                                          |
;+--------------------------------------------------------------------------+
;|     Don't be a LAMER !!!!! Don't use this code to make your own BBS      |
;|               advertisement or I 'll have to kill you !!! :)             |
;*--------------------------------------------------------------------------*

; This will run on a 286 or better with or without a VGA and ADLIB, of
; course, it will looks better if you have both :)

; I tried to optimize the code for both speed and size, but as I don't know
; so much about optimization, I think I don't optimized it anyway... :)

Extrn hscplayer:far               ; Define some externals for the HSC-PLAYER
Extrn hsc_equalizer:byte

pilha segment word stack 'stack'  ; Start Stack segment
      db 256 dup(?)
pilha ends                        ; End Stack Segment

; End of idiot comments :)

codigo segment word public 'code'
       assume cs:codigo,ds:codigo,ss:pilha

start:
        push    cs          ; Make DS = CS
        pop     ds          ;

        Mov     ax,0003h    ; Change to mode 03, No no, it's not $13 !
        Int     10h         ; I want TEXT MODE :)

        clc                 ; Clear Carry
        mov ah,04h          ; Function #04 of HSCPLayer (check for sound card)
        call hscplayer      ; Call it
        jc   no_adlib       ; Ops... there is no Adlib here...
        mov  [sound],01h    ; Yes, Sound Ok, so move 1 to sound...

        xor ax,ax           ; Function 0 of HSCPLayer
        mov bx,ax           ; BX = 0
        push cs             ; Make ES = CS
        pop  es
        lea si,musica       ; Get the Music Offset
        call hscplayer      ; And call the player

        mov ah,06           ; Link User IRQ (HscPlay function)
        lea dx,VUs          ; Put Proc Offset here
        mov bl,1            ; 1 to Link
        call hscplayer      ; Call the player

no_adlib:

        Mov     Vga,00     ; I think there is no VGA here...
        Mov     ax,1A00h   ; Check for VGA
        Int     10h
        cmp     bl,7       ; Display active code
        je      IsVGA      ; It will work...
        cmp     bl,8       ; Display active code
        jne     No_VGA     ; No VGA :-(

IsVGA:  Mov Vga,1           ; Well, as we have VGA, put 1 on it...
        mov [linha],400     ; Put the screen Up... we have to change it...
        Call ScreenToLine   ; DO it !

No_VGA:
        lea  si,tela        ; SI = OFFSET TELA (Intro Screen Crunched)
        xor  di,di          ; DI = 0000h
        mov  ax,0b800h      ; AX = Screen Segment
        mov  es,ax          ; ES = AX
        mov  cx,tela_length ; Length of the screen DATA
        Call Uncrunch       ; Call the uncrunch routine

        cmp     Vga,0       ; Is there a VGA here ?
        jne     Skip1       ; NO !!!! arrrrrrrgggh :-((((
        jmp     NotVGA

skip1:
        mov     dx,03c8h    ; Change color 5 to bright WHITE
        mov     al,05
        out     dx,al
        inc     dl
        mov     al,63
        out     dx,al
        out     dx,al
        out     dx,al

        Mov     ah,02h      ; BIOS function 02
        Xor     bh,bh       ; Page number 00
        Mov     dh,25*2     ; Put the cursor on row 50 to hide it
        int     10h         ; Yes, I'm using BIOS to do this...

;====================================================================
; The Bouncing Screen is here !
;====================================================================

Volta3:
        Mov  speed,01       ; Start with Speed 1

Desce:  Call ScreenToLine   ; Put Screen in the right start line
        mov  ax,[speed]     ; Get the speed
        mov  bl,03          ; and divide it
        div  bl             ; by 3
        xor  ah,ah          ; zero the remainder
        sub  [linha],ax     ; sub it from linha
        inc  [speed]        ; up the speed by one
        cmp  [linha],00     ; Is the line 0 ??
        jg   Desce          ; No, it's biggest, so LOOP again...

        Mov ah,01h          ; We must check if
        Int 16h             ; the user pressed a key
        Jz  Sobe            ; No, so continue
        Mov ah,00           ; Yes, so extract it from the
        Int 16h             ; Keyb Buffer
        Jmp  Go_Out         ; And GO OUT !!!!

Sobe:   mov  ax,[speed]     ; Get the speed again
        shr  ax,02          ; Hummm... divide it by 4
        add  [linha],ax     ; Add the result to line
        dec  [speed]        ; Down the speed by one
        Call ScreenToLine   ; Put the screen in the right line start
        cmp  [speed],6      ; Is the speed greater than 6
        jg   Sobe           ; Yes, so, LOOP again...

        dec [kicks]         ; Decrement the kicks number
        jnz volta3          ; Is it 0 ? No, so, LOOP ALL again...

Go_Out:

        Mov [linha],00      ; Put the screen on the right place
        Call ScreenToLine   ; Do it !

;====================================================================
; Here begins the MAIN LOOP...
;====================================================================


Loop_E:

        mov  cx,80                  ; The screen has 80 columns
        mov  ah,14                  ; We want yellow text
        mov  dx,[textcol]           ; Move the start char pos to dx

bla4:
        mov  si,dx                  ; SI want to be the start char pos
        xor  di,di                  ; DI has to start in 0 (first column)
        cli                         ; Disable Interrupts
        Call retrace                ; Again call the Wait for Retrace
bla2:   mov  al,texto [si]          ; Get char
        mov  es:word ptr [di],ax    ; ES points to B800 (Screen)
        add  di,2                   ; Next column on the screen
        inc  si                     ; Point to next char

        cmp  di,2*80                ; End of line ?
        jb   bla2                   ; No, loop...

        inc  dx                     ; Inc Text Column start
        cmp  dx,80*8                ; End of text ?
        jb   bla3                   ; No, so loop...
        xor  dx,dx                  ; Yes, so, Start Column = 0
bla3:
        sti                         ; Enable interrupts
        loop    bla4                ; Loop if not finished the 80 charac.
        mov     textcol,dx          ; Save the last char pos

;====================================================================
; Preparing to change the PAL...
;====================================================================

        mov     cl,0ffh     ; Color fade counter
        mov     dh,03h      ; See below

ChangePal:

        Call retrace        ; Wait for retrace

        mov     dl,0c8h     ; DX = 03c8h = Preparing to change the pallete
        mov     ax,05       ; Color 05
        out     dx,al       ;
        inc     dl          ; DX = 03c9h = Now we will change the r,g,b
        mov     al,cl       ; Get CL and div it
        shr     ax,2        ; by 4 to shade the color
        xor     al,[temp]   ; down to 0 OR up to 64
        mov     r,al        ; Put the new color in r
        mov     g,al        ; Put the new color in g
        mov     b,al        ; Put the new color in b
        mov     al,color    ; Color counter (starts with 2)
        shr     al,1        ; Div it by 2
        dec     al          ; And dec it, so, the first time it will be = 0
        mov     si,ax       ; Put it in SI
        mov     [r+si],63   ; And write the fixed color on the right place
        mov     al,r
        out     dx,al       ; Change RED
        mov     al,g
        out     dx,al       ; Change GREEN
        mov     al,b
        out     dx,al       ; Change BLUE
        loop    ChangePal

        dec    cx           ; DEC the color fade counter
        inc    color        ; increment the color counter
        cmp    color,8      ; compare it with 8 = r,g,b were done
        jb     bla1         ; No, don't change
        mov    color,2      ; Starting with 2 again...

bla1:
        xor   [temp],0ffh

        Mov ah,01h              ; Again this shit for key check...
        Int 16h
        Jnz Skip                ; Blah, key pressed... jump to end...
        Jmp Loop_E              ; No key pressed... Loop back !

skip:   Xor ax,ax               ; Extract key from keyb buffer
        Int 16h

        mov     linha,00        ; Make linha = 0

gezzz:  Call    ScreenToLine    ; Put the screen in line linha
        add     linha,5         ; Up the screen 5 lines
        cmp     linha,400       ; Compare with 400 lines
        jb      gezzz           ; If is below loop...

        cmp [sound],00      ; Is there AdLib here ?
        je  no_music        ; No, so skip the next part...

        mov ah,06           ; Link User IRQ (HscPlay function)
        lea dx,Vus          ; Put Proc Offset here
        xor bl,bl           ; 0 to UnLink
        call hscplayer      ; Call the player

        mov ah,03           ; FadeOut the Music...
        Call hscplayer      ; Do it !

no_music:

        xor     di,di       ; Clear the screen
        mov     ax,0015     ; We want a blank screen
        mov     cx,2000     ; 2*2000 bytes
        rep     stosw       ; Repeat it 2000 times


        mov ah,09h          ; Print the end string
        lea dx,mesg         ; offset of the string
        int 21h             ; Arghh... DOS function...


gezzz2: Call ScreenToLine   ; Put the screen down
        sub  linha,3        ; Sub 3 lines each time
        cmp  linha,0        ; Is the line 0 ?
        jg   gezzz2         ; No, it's greater so, loop....

        mov  linha,0        ; Make sure it stoped in the line 0
        call screentoline   ; Put it in the right start point...


        mov     dx,03c8h    ; Back color 5 to the original values
        mov     al,05
        out     dx,al
        inc     dl
        mov     al,42
        out     dx,al
        mov     al,00
        out     dx,al
        mov     al,42
        out     dx,al

bla6:

        Cmp [sound],00h         ; There is song rolling ?
        Je  Gooo                ; No, so go out !
        Mov ah,02h              ; Yes, so, Stop it !
        Call hscplayer          ; Stop it now !
        Jmp Gooooo

Gooo  :
        mov ah,09h              ; Print String Function
        lea dx,mesg3            ; message offset
        int 21h                 ; Arghh... DOS function again...

Gooooo:
        mov     ax, 4c00h       ; Ufh... get out...
        int     21h

NotVga:
        xor ax,ax           ; Wait for keypress function
        int 16h             ; Call int16h

        mov ah,02             ; Set Cursor Position Function
        mov bh,0              ; Display Active Page
        mov dx,(24 shl 8)+80  ; ROW 24, COL 80
        int 10h               ; Call Int 10h !

        mov ah,09h          ; Print String Function
        lea dx,mesg2        ; message offset
        int 21h             ; Arghh... DOS function again...
        jmp bla6            ; Go to see if there is song rolling

;====================================================================
; Ok, now we have the little proc to draw the BARs on the screen
; This proc is called by HSCPLAY at each music interrupt update...
;====================================================================

VUs Proc Far

         mov ax,seg HscPlayer   ; Make DS = Segment of HSCPlayer
         mov ds,ax

         mov ax,0b800h          ; ES points to screen segment
         mov es,ax

         xor si,si              ; SI = number of music tracks (VUs)

NextBar:
         mov ax,05              ; 5 caracters by each bar
         mov bx,si              ; Put actual bar in BX
         inc bl                 ; Increment it (we have to start with 1)
         mul bl                 ; And multiply to get the right column
         shl ax,1               ; Mul it by 2 (we need to count the atrib too)
         add ax,(9*160+1)+16    ; Start it in the atribute of column 16
         add ax,si              ; Let put a space between each bar
         add ax,si              ; yes... 2 times to work right ...

         mov di,ax              ; Ok, the column value in DI (bh=0)

         Mov bl,[hsc_equalizer+si]  ; Get the BAR VALUE
         mov cx,0fh+1               ; CX = max bar value (0fh) + 1
         inc bl                     ; Increment the bar value

DrawOneBar:
         Mov  dl,00010000b          ; This is the color for the BAR
         Cmp  bl,cl                 ; What do we have to do ?
         Ja   jmp1                  ; Hummm... Draw it
         Xor  dl,0ffh               ; Hummm... Erase it
         And  es:byte ptr [di  ],dl ; Erase this piece of the bar
         And  es:byte ptr [di+2],dl ; Next column
         And  es:byte ptr [di+4],dl ; Next column
         And  es:byte ptr [di+6],dl ; Next column
         And  es:byte ptr [di+8],dl ; Next column
         Jmp  jmp2                  ; Jump... we don't want to draw the bar
jmp1:
         Or es:byte ptr [di],dl     ; Draw this piece of the bar
         Or es:byte ptr [di+2],dl   ; Next column
         Or es:byte ptr [di+4],dl   ; Next column
         Or es:byte ptr [di+6],dl   ; Next column
         Or es:byte ptr [di+8],dl   ; Next column
jmp2:
         add  di,160          ; Go to next line (2*80)
         loop DrawOneBar      ; Loop if the bar isn't finished yet...

         inc  si              ; Get next bar
         cmp  si,9            ; Is it the last ?
         jb   NextBar         ; No, so LOOP again...

         ret
VUs Endp


;===================================================================
; This procedure shows the screen starting on the line specified in
; LINHA. The VGA screen standard mode has 400 lines / screen !
; This is the "funny" piece of the code... want more comments ?
; Write them :) There is a lot of VGA REGISTERS being changed here,
; if you don't know, you can "tell" to VGA start the screen display
; at any line you want (line = pixel line, not TEXT line), so you
; can make a smooth scrolling efect, like I did here...
;===================================================================

ScreenToLine Proc near  ;

        CLI                     ; Disable Interrupts

        mov dx,03dah
@loop1: in  ax,dx
        and ax,08
        cmp ax,08
        je @loop1

        mov  ax,[linha]         ; Move the line to ax
        shr  ax,4               ; Divide the line number by 16 (char size)
        mov  bl,80              ; Mul it by the screen horiz size (in columns)
        mul  bl
        mov  bx,ax              ; Put the result in bx

        Mov dl,0d4h
        Mov ax,0ch
        out dx,ax
        mov al,bh
        Inc dx
        out dx,al

        Dec dx
        Mov ax,0dh
        out dx,ax

        Inc dx
        mov al,bl
        out dx,al

        mov dl,0dah
@loop2: in  ax,dx
        and ax,08
        cmp ax,08
        jne @loop2

        mov dl,0d4h
        mov al,08h
        out dx,al

        mov ax,[linha]
        and ax,0fh
        Inc dx
        out dx,al

        STI                     ; Enable interrupts

        Ret

linha   dw 00

ScreenToLine endp



;===================================================================
; Powerful procedure to wait for retrace :))
;===================================================================

Retrace Proc near
    push dx                 ; You know what it does, not ?
    push ax                 ; Again...
    mov  dx,3dah            ; No, no comments for this one...
VRT:
    in      al,dx
    and     al,8
    jnz     VRT             ; Wait until Verticle Retrace starts
NoVRT:
    in      al,dx
    and     al,8
    jz      NoVRT           ; Wait until Verticle Retrace Ends
    pop ax                  ; Restore all the shit...
    pop dx
    RET
Retrace endp


;===================================================================
; The next PROC is the THEDRAW UNCRUNCH ROUTINE to UNPACK THE SCREEN
;===================================================================
UNCRUNCH PROC NEAR
;Parameters Required:
;  DS:SI  Crunched image source pointer.
;  ES:DI  Display address pointer.
;  CX     Length of crunched image source data.
;
       PUSH    SI                      ;Save registers.
       PUSH    DI
       PUSH    AX
       PUSH    BX
       PUSH    CX
       PUSH    DX
       JCXZ    Done

       MOV     DX,DI                   ;Save X coordinate for later.
       XOR     AX,AX                   ;Set Current attributes.
       CLD

LOOPA: LODSB                           ;Get next character.
       CMP     AL,32                   ;If a control character, jump.
       JC      ForeGround
       STOSW                           ;Save letter on screen.
Next:  LOOP    LOOPA
       JMP     Short Done

ForeGround:
       CMP     AL,16                   ;If less than 16, then change the
       JNC     BackGround              ;foreground color.  Otherwise jump.
       AND     AH,0F0H                 ;Strip off old foreground.
       OR      AH,AL
       JMP     Next

BackGround:
       CMP     AL,24                   ;If less than 24, then change the
       JZ      NextLine                ;background color.  If exactly 24,
       JNC     FlashBitToggle          ;then jump down to next line.
       SUB     AL,16                   ;Otherwise jump to multiple output
       shl     al,5                    ;routines.
       AND     AH,8FH                  ;Strip off old background.
       OR      AH,AL
       JMP     Next

NextLine:
       ADD     DX,160                  ;If equal to 24,
       MOV     DI,DX                   ;then jump down to
       JMP     Next                    ;the next line.

FlashBitToggle:
       CMP     AL,27                   ;Does user want to toggle the blink
       JC      MultiOutput             ;attribute?
       JNZ     Next
       XOR     AH,128                  ;Done.
       JMP     Next

MultiOutput:
       CMP     AL,25                   ;Set Z flag if multi-space output.
       MOV     BX,CX                   ;Save main counter.
       LODSB                           ;Get count of number of times
       MOV     CL,AL                   ;to display character.
       MOV     AL,32
       JZ      StartOutput             ;Jump here if displaying spaces.
       LODSB                           ;Otherwise get character to use.
       DEC     BX                      ;Adjust main counter.

StartOutput:
       XOR     CH,CH
       INC     CX
       REP STOSW
       MOV     CX,BX
       DEC     CX                      ;Adjust main counter.
       LOOPNZ  LOOPA                   ;Loop if anything else to do...

Done:  POP     DX                      ;Restore registers.
       POP     CX
       POP     BX
       POP     AX
       POP     DI
       POP     SI
       RET
UNCRUNCH ENDP



speed    dw 0000
temp     db 00
r        db 00          ; Saves the value of RED
g        db 00          ; Saves the value of GREEN
b        db 00          ; Saves the value of BLUE
textcol  dw 00          ; Pointer to the scrolling text
Vga      db 00          ; Sound MUST follow VGA
Sound    db 00          ; Don't change the position or it won't work !
color    db 02          ; A little color counter to make the things right
kicks    db 15          ; How many times the screen will bounce ?

include c:\code\great\great2.mus              ; Include the music
include c:\code\great\teltest.asm             ; Include the ANSI Screen

texto   db 80 dup (' ')
db '         WARMBOOT BBS - A Maior Base de DEMOs de MultiMedia do Brasil !         '
db '               -> * 3 Gigas de arquivos - 1 GIGA ON-LINE * <-                   '
db '    *  Suporte autorizado do ARJ, Inertia Player, Dual Mod Player e EGOF *      '
db '     MEGAs de arquivos de Som, M£sica, C¢digos de DEMOS, Efeitos Gr ficos,      '
db '     Protrackers, Utilit rios, GIFs, Programa‡„o MultiMedia e muito mais !!!    '
db '  Representantes exclusivos dos grupos FUTURE CREW, RENAISSANCE e TWILIGHT ZONE '
db '         Ligue e confira : (0194) 26-5112 - 24horas - USR 14.4K (8,N,1)         '
db 80 dup (' ')

mesg       db 13,10,'Coded by WarmBooter /Wá  (1994) !',13,10,'$'
mesg2      db ' Go and buy a VGA to see the real power of GREAT2 !',13,10,'$'
mesg3      db 'Want music ? Go and buy a SB now !',13,10,'$'

codigo ends
end start

;-------------------------------------------------------------------------
; This code (ALL THE CODE, everything but the THEDRAW UNPACK ROUTINES and 
; the PLAYER) was made by ME ! It is freeware but you can't use it to do
; your Intro ! BE CREATIVE !!!!
;-------------------------------------------------------------------------
; Before finish this I would like to ask to these great musicians around
; the World (PM, Skaven, etc...) to stop to make these technos, it sucks !!!
; Make something more heavy !
;-------------------------------------------------------------------------

Share and Enjoy !

Deixe um comentário