Metropoli BBS
VIEWER: 386video.asm MODE: TEXT (ASCII)
include 386power.inc
include 386video.inc

; BASIC VIDEO SERVICES
; _SetGameMode
; _RestoreTextMode
; _WaitVR
; _DisplayStart
; _PageFlip

code32 segment para public use32
       assume cs:code32,ds:code32
       
_ActiveBase dd 0 ;base of page to write to
 MidBase    dd 0 ;base of "in between" page
_ViewBase   dd 0 ;base of page to show
_ScoBase    dd 0 ;base of "scoreboard" panel

; index of row start offsets from base of display screen (active,view,score)
_RowStart   dd 0
            dd VYHEIGHT dup(0)

; sottrai VGARAWOFFSET per passare da pointer code32 relativo
; ad offset nella vga memory window
VGARAWOFFSET dd 0

  DispOffset      dd 0  ; offset di scrolling
; old code for pix panning  
; SmoothPix       dd 0  ; valore per smooth scrolling

; XVGA MODE ENTRY/EXIT ROUTINES

;----------------------------------------------------------------------------
; void SetGameMode;
;
             public _SetGameMode
_SetGameMode:
             
                pushad
                

                mov V86ax,0013h    ; set 256-color mode BIOS MODE 13h
                mov al,10h         ;
                int 33h            ;

		mov dx,SEQUENCER    ; convert to X-mode addressing
                ; sequencer register 04h (MEMORY MODE)
                ; set chain mode off,no odd/even,more than 64k,graphics mode
                mov ax,0604h        ;
                out dx,ax           ;
                
                WRITEMODE  WPUT
                WRITEPLANE WPA

                @rlp edi,000A0000h
                mov  ecx,16*1024     ; CLEAR VIDEO MEMORY
                xor  eax,eax         ; (addressing mode is planar
                rep stosd            ;  but raster scanning is still the
                                     ;  same of mode 13h)
                                     
                ; now change the raster scanning method
		mov dx,CRTC

                mov ax,00014h       ; TURN OFF DWORD MODE (CRTC LONG MODE)
                out dx,ax           ; (change vram scanning method)

                mov ax,0E317h       ; TURN ON BYTE MODE
                out dx,ax           ;
                
                ; Tweaked mode fully activated, now perform other stuff
                
		mov al,LINE_OFS
                mov ah,SXWIDTH ; ampiezza di una linea/8
                out dx,ax
                
                ;mov dx,GRAPHICS   ; superfluo se non sei paranoide
                ;mov ax,ALL_BIT
                ;out dx,ax
                


; inizializza variabili "interne" ed "esterne"
                @rlp  eax,0A0000h
                mov VGARAWOFFSET,eax
                @rlp eax,SCOBASE
                mov _ScoBase,eax
                @rlp eax,BASE0
                mov _ViewBase,eax
                @rlp eax,BASE1
                mov MidBase,eax
                @rlp eax,BASE2
                mov _ActiveBase,eax
;                xor eax,eax
                 mov DispOffset,eax
;                mov SmoothPix,eax

                mov edi,offset _RowStart  ; Row offset table 
                mov ecx,VYHEIGHT          ;
                mov eax,0                 ;
                mov ebx,PXWIDTH           ;

iloop:
                stosd
                add eax,ebx
		loop iloop
                
                mov ecx,LYHEIGHT
                call SplitScreen
                call InstallPFLP
                call _PageFlip
; ritorna a chi ha chiamato
                popad
		ret
        
;----------------------------------------------------------------------------
; void RestoreTextMode( void )

		public  _RestoreTextMode
_RestoreTextMode:
                mov  V86ax,0003h        ; set text mode
                mov al,10h
                int 33h
		ret



;----------------------------------------------------------------------------
; void _WaitVR(void)	WAIT A FULL V.R TIME FRAME
;

		public _WaitVR
_WaitVR:        push edx
                push eax
		mov     dx,STATUS
@@swap_retr1:   cli
		in      al,dx
		test	al,8  ; VERTICAL RETRACE ?
		jnz	@@swap_retr1  ; loop if V.R. (into retrace)
@@swap_retr2:
		in      al,dx
		test	al,8  ; V.R. ?
		jz	@@swap_retr2  ; loop if not V.R.
                sti
                pop eax
                pop edx
		; NOW you have a FULL V.R. time frame available
		ret



;----------------------------------------------------------------------------
; void DisplayStart(eax= x,edx =y)  SET DISPLAY VIDEO WINDOW POSITION
;                                   INSIDE DISPLAY PAGE

		public _DisplayStart

_DisplayStart:
                ; eax= x_position , ebx = y_position
                ;;push ecx
                push eax
                ;;mov ecx,eax        ;
                ;;and ecx,3          ; maschera di bits per lo shifter
                ;;shl ecx,1          ;
                ;;mov SmoothPix,ecx  ;

                shr eax,2                ;offset nella pagina video corrente
                add eax,_RowStart[edx*4] ;
                mov DispOffset,eax       ;
                pop eax
                ;;pop ecx
		ret


;----------------------------------------------------------------------------
; void DisplayPage(ECX= offset of display page )  
; SHOW PAGE starting at offset eax  (INTERNAL FUNCTION)

DisplayPage:
                ; ecx = offset di inizio pagina video corrente
                mov     bx,VSTART_HILO   ;preload for fastest access
                add     ecx,DispOffset   ;  somma l' inizio video nella pagina
                sub     ecx,VGARAWOFFSET ; trasforma in raw vga offset
                xchg    bh,cl    ; BX=prima coppia CX=seconda coppia

		mov     dx,STATUS

		cli
;@@WaitNVS:
;                in      al,dx
;                and     al,08h
;                jnz     @@WaitNVS  ;vertical sync is active high (0= drawing)
@@WaitHS:
                in      al,dx
                and     al,01
                jnz     @@WaitHS  ;hor. sync is active high (1 = active)
                ; n.b. HSync is display enable too!!!
                
		; Set the start offset in display memory of the page to display.
		mov     dx,CRTC
                mov     eax,ebx
		out     dx,ax   ;start address low
                mov     eax,ecx
		out     dx,ax   ;start address high

;                mov dx,STATUS
;@@WaitVS:
;                in      al,dx
;                and     al,08h
;                jz     @@WaitVS  ;vertical sync is active high (1 = active)
;@@QuickFlip:
;                ; attribute controller already in address mode
;                mov     dx,ATTRIBUTE
;                mov     al,PEL_PANNING
;                out     dx,al
;                mov     al,byte ptr SmoothPix
;                
;                ; now give some time to old jerky vga cards
;                jc  zik
;                zik:
;                jnc zok
;                zok:
;                jc  zik2
;                zik2:
;                jnc zok2
;                zok2:
;                jc  zik3
;                zik3:
;                jnc zok3
;                zok3:
                
		out     dx,al
		sti
                
		ret

;--------------------------------------------------------------------------
; void cdecl PageFlip(void)
;
	  public _PageFlip

_PageFlip: ; _Viewbase <-- _ActiveBase <-- MidBase <-- _ViewBase
           ; & show new _ViewBase
          pushad
          mov ebx,PageTicks
@pfgloop:
          mov eax,_TimerTicks
          mov edx,eax
          sub eax,ebx
          cmp eax,FrameTicks
          jb @pfgloop   ; loop if too few time has passed
          mov PageTicks,edx          
          
          mov  eax,MidBase
          mov  ebx,_ViewBase
          mov  ecx,_ActiveBase
          mov  _ActiveBase,eax
          mov      MidBase,ebx
          mov    _ViewBase,ecx
          ; ecx == new viewbase
	  call DisplayPage
          popad
	  ret

;--------------------------------------------------------------------------
; void cdecl SplitScreen(ecx = Splitline)
; INTERNAL FUNCTION

SplitScreen:
        ; ecx = SplitLine (in low word)

	; inanzitutto disabilita il panning
        ; dello splitscreen, ma prima salva il parametro
        push ecx
        cli

        mov dx,STATUS ;poni attribute controller in address mode
        in al,dx
        
        mov ecx,200
okuto1: loop okuto1

	; ora disabilita il panning dello splitscreen
        mov al,ATTR_CONTROL
        mov dx,ATTRIBUTE
        out dx,al          ; seleziona registro di panning
        
        ; lascia passare del tempo
        mov ecx,200
okuto2: loop okuto2
        
        inc dx
        in al,dx ;leggi gli attributi correnti
        or al,NOSPLITPAN
        
        ; lascia passare del tempo
        mov ecx,200
okuto3: loop okuto3

        mov ah,al ; salva i flag
        
        mov dx,STATUS ;
        in al,dx      ; nuovamente in address mode (ripulisci il f/f di stato)
        
        mov dx,ATTRIBUTE
        mov al,ATTR_CONTROL ;pronto a ri-scrivere su attribute controller
        out dx,al
        
        ; lascia passare del tempo
        mov ecx,200
okuto4: loop okuto4        

        mov al,ah   ; scrivi i nuovi flag degli attributi video
        out dx,al   ;

        sti

	; ora puoi modificare il line compare
	; register e gli altri suoi bits
	; sparsi in altri 2 registri
        pop ecx
	; Now wait for vertical sync, so the other page will be invisible when
	; we start drawing to it.
	mov     dx,STATUS
        cli
@@WVS:
	in      al,dx
        and     al,08h
	jz      @@WVS  ;vertical sync is active high (1 = active)

	mov dx,CRTC
        
        ;  ECX = splitline
        
        shl ecx,1  ; moltiplica per due
                   ; (si e' in modo a doppia scansione)
        dec ecx    ; e riduci di 1

	; write bit 0..7
	mov al,LINE_COMPARE
        mov ah,cl
        out dx,ax

	; write bit 8

        mov cl,0

        shr ecx,1  ; porta il bit 8
        shr  cl,3  ; nel bit 4 di BL

        mov ax,0707h  ;bit 8 is CRTC INDEX 7 BIT 4
        out dx,al
        inc dx
        in al,dx
        dec dx
        and al,0EFh ;azzera bit 4
        or al,cl  ; registra il nuovo bit
        xchg ah,al
        out dx,ax

        ; write bit 9
        mov cl,0
        shr ecx,1
        shr cl,1
        mov ax,0909h
        out dx,al
        inc dx
        in al,dx
        dec dx
        and al,0BFh ;azzera bit 6
        or  al,cl
        xchg al,ah
        out dx,ax

	sti ;enable interrupts
	ret

; EXTENDED INPUT, TIMER DRIVEN PAGE FLIPPING & SOUND OUTPUT
; questa sezione di 386VIDEO(game)
; gestisce in modo diretto la tastiera,lo speaker incorporato
; in ogni PC &  il timer connesso a IRQ0.


; RAW KEYBOARD I/O
; 32bit section, Keyboard ISR for raw keyboard input

; Raw KeyBoard table
; Every key "description" is made of two consecutive bytes
; if a key has a keyboard scancode K
; address:     
; _RKB+(K)  bit   meaning:       description:
;             0   IS_PRESSED     0 == key K is currently not pressed
;                                1 == key K is currently     pressed
;
;             1   TOUCHED        0 == key K has not been pressed 
;                                     since last time you cleared this bit.
;                                1 == key K has been pressed
;
; Use IS_PRESSED for "raw" control (i.e cursor keys in a game)
; and TOUCHED (but clear it before!!) for "keyboard-like"  control
; when you have to choose items in a menu or when
; you have to check special "toggle" keys that may be pressed anytime
; (i.e. the all time high ESC key)    
        public  _RKB,_RKBPressed
_RKB    db      0    
        db      128 dup(0)

_RKBPressed db  0 ; 0 == no keys pressed since last time you reset this flag
                  ; 1 == something happened, a key has been pressed
                  ; (this is like a "general" TOUCHED flag)

IRQ1_ISR:
        cli
        push    eax
        push    ebx
        push    ds
        mov     ds,cs:_SelData

	in      al,60h          ; get scan code

        movzx   ebx,al          ; move scan code to index register

	in      al,61h          ; get control code
        push    eax
	or      al,80h          ; clear keyboard of interrupt:
	out     61h,al          ;
        pop     eax             ;   first send control byte with inverted MSB
	out     61h,al          ;   then send plain control byte

        mov     al,EOI          ; send generic EOI to
        out     PIC0_CTRL,al    ;   PIC
				; enabling other interrupts
        mov al,bl
        and bl,07Fh
	; if the key was released, the high bit is set in the scan code
        mov byte ptr[ebx+offset _RKB],02h ; reset "Is being pressed" flag
                                          ; and left set "Has been pressed"
        
        rol al,1
        jc  key_released
        mov byte ptr [ebx+offset _RKB],03h ; set "Is being pressed" flag
                                           ; and "Has been pressed" flag
key_released:
        mov _RKBPressed,1     ; state of keyboard has changed
        sti
        pop ds
        pop ebx
        pop eax
        iretd 

        public _WaitKey
_WaitKey:
        ; WAITS UNTIL A KEY IS PRESSED
        mov _RKBPressed,0
@waitmore:        
        test _RKBPressed,1
        jz @waitmore        
        ret
        
        public _InstallRKB
_InstallRKB:
        pushad
        mov eax,0
        mov edi,offset _RKB
        mov ecx,32 ; 128bytes == 32 dwords
        rep stosd
        cli
        test _386Man,IS_DPMI
        jnz NoReflex
        mov al,21h
        mov V86ax,2509h
        mov V86ds,seg code16
        mov V86dx,offset IRQ1_RISR
        int 33h
NoReflex:        
        mov bl,1
        mov edx,offset IRQ1_ISR
        call _SetIRQ
        sti
        popad
        ret

; PWM SOUND ON PC-SPEAKER & TIMER-DRIVEN PAGE FLIPPING
; 32bit section

        public  _SoundOn,_SoundLen,_SoundPtr
        public  _DSoundOn,_DSoundLen,_DSoundPtr
        
        align dword
; current sound data        
_SoundLen   dd      0  ; default = zero sound lenght
_SoundPtr   dd      0  ; default = null, points to already decode sound data
                       ; the installed sound driver must play
; default sound data                       
_DSoundLen  dd      0  ; default = zero , default sound lenght                                     
_DSoundPtr  dd      0  ; default = null , default sound

         align byte
         
_SoundOn  db 0 ; Set this to turn on sound generation, all sound driver must
               ; refer to this flag to control sound output
_DSoundOn db 0 ;ditto for the default sound insertion at end of sound
                          

 Buzzing    db      0 ; are we currently producing sound ? (private var.)
 
          align dword
; decrementi di contatore usati come riferimento
PWMTICK   = 149           ; conteggio equivalente a 8008Hz
VSyncTick dd 0            ; Vsync count ... Initialize with VSyncInit()
TIMERTICK = (64*1024)     ; conteggio equivalente a 18.2 Hz
ONESECOND = 1193180

        public _TimerTicks,_SysTicks,_Seconds
        
_TimerTicks dd 0 ; ticks in frequenza base a 1193180 Hz
                 ; usa sempre la granularita piu fine a disposizione
                 
 PageTicks  dd 0 ; tick counter for pageflips
 FrameTicks  = 19886 ; 1193180hz/60hz timer count
                 
 InnerTicks dd 0 ; simile a _TimerTicks, ma usato per calcolare systicks
 SecTicks   dd 0 ; idem, ma per _Seconds
 
_SysTicks dd 0  ; ticks a 18.2Hz      
_Seconds  dd 0  ; contasecondi totali

         align byte

InstallPFLP:
        ; installs the pageflip support code
        pushad
        cli
        ; first in real mode
        test _386Man,IS_DPMI
        jnz @nopfreflex
        mov al,21h
        mov V86ax,2508h
        mov V86ds,code16
        mov V86dx,offset PFLP_RISR
        int 33h
@nopfreflex:        
        ; second in protected mode (it works only this way under DPMI)
        mov bl,0
        mov edx,offset PFLP_ISR
        call _SetIRQ
        mov al,T_SET0   ; set programmable interval timer 0 to FrameTicks
        out PIT_CTRL,al ;
        mov eax,FrameTicks
        out PIT0,al     ;
        mov al,ah       ;
        out PIT0,al     ;
        ; initialize counters
        mov _TimerTicks,0
        mov PageTicks,0
        mov InnerTicks,0
        mov SecTicks,0
        mov _SysTicks,0
        mov _Seconds,0
        sti
        popad
        ret         
        
PFLP_ISR: 
        sti
        push    eax
        push    ebx
        push    ds

        mov     ds,cs:_SelData
        mov ebx,FrameTicks
        add _TimerTicks,ebx
        add  InnerTicks,ebx
        add    SecTicks,ebx
        mov eax,SecTicks
        sub eax,ONESECOND
        jb pdone_sec
        mov SecTicks,eax
        inc _Seconds
pdone_sec:        
        
        mov eax,InnerTicks
        sub eax,TIMERTICK
        jb pdone_irq0
        mov InnerTicks,eax
        ; n.b. TTick conta i ticks di sistema in formato fixed point
        ;      con una cifra decimale
        inc _SysTicks
pzapped_time:
        ; it's time to call the old ISR
        mov al,8
        int 33h
        jmp short pirq0_digged
pdone_irq0:        
        mov al,EOI
        out PIC0_CTRL,al
pirq0_digged:        
        pop ds
        pop ebx
        pop eax
        iretd ; ritorna il controllo a 386 power

        public _InstallPWM
_InstallPWM:
        ; install irqs & set up voc_table
        pushad
        ; initialize internal vars
        mov Buzzing,0
        mov _SoundOn,0
        mov _DSoundOn,0
        mov _SoundLen,0
        mov _SoundPtr,0
        mov _DSoundLen,0
        mov _DSoundPtr,0
; set voc_table
        mov esi,offset pwm_table
        mov edi,offset voc_table
        mov eax,0
        mov ecx,64
set_up_voc_table:        
        movsd
        loop set_up_voc_table
        cli
        ; first install the real mode side
        cmp _386Man,IS_DPMI
        je @nopwreflex
        mov al,21h
        mov V86ax,2508h
        mov V86ds,code16
        mov V86dx,offset code16:IRQ0_RISR
        int 33h
@nopwreflex:        
        ; then the protected mode side (it works only this way in DPMI)
        mov bl,0
        mov edx,offset IRQ0_ISR
        call _SetIRQ
        mov ebx,F8008 ; 8008Hz count
        cmp _386Man,IS_DPMI
        jne @vcpispeed
        shl ebx,1 ; freq to 4004Hz
@vcpispeed:        
        mov play_rate,ebx ; set playback frequency for voc converter
        
        mov al,T_SET0   ; set programmable interval timer 0 to 8008Hz or 4004Hz
        out PIT_CTRL,al ;
        mov al,bl       ;
        out PIT0,al     ;
        mov al,bh       ;
        out PIT0,al     ;
        
        sti
        ; no need to initialize time counters
        ; InstallPFLP already did it
        popad
        ret         
        
; PWM SOUND SPEAKER DRIVER

 zarp_sound:
        cmp _DSoundOn,0
        je dont_buzz
        mov eax,_DSoundLen
        dec eax
        mov ebx,_DSoundPtr
        mov _SoundLen,eax
        jmp doit_again
        
 dont_buzz:
        cmp Buzzing,0
        je getoutsound
        mov Buzzing,0
        ; turn speaker OFF
        in      al, 61h                 ;Read I/O port B into AL
        and     al, 11111100b           ;mask lower two bits
        out     61h, al                 ;to turn off speaker
        jmp short getoutsound

DoTheSound macro
        cmp _SoundOn,0
        je  dont_buzz    ; no sound if sound flag is off
        cmp _SoundLen,0
        je  zarp_sound   ; no sound if at end of "soundtrack"
        dec _SoundLen    ; decrease "soundtrack" lenght

        mov     ebx,_SoundPtr    ;get current count pointer
doit_again:
        mov     al,[ebx]            ; count into AX
        inc     ebx                 ; & update sound pointer
        mov     _SoundPtr,ebx       ; (only offset is changed)
        mov     ah,al    ; copy count 
        or      al,al         ; zero count sets speaker off
        ;je      dont_buzz     ;
        
        mov     al,P_SET2S
        out     PIT_CTRL,al      
        mov     al,ah
        out     PIT2, al             

        cmp Buzzing,0
        jne getoutsound
        mov Buzzing,1
        ; turn sound on
        in      al, 61h                 ;read I/O port B into AL
        or      al,3                    ;turn on bits 0 and 1
        out     61h,al                  ;to turn on speaker
getoutsound:
        endm

; IRQ0 Timer handler, it handles TWO different "time driven" nested loops
; the 8008 Hz  PWM sound system
; the 18,2 Hz System tick for periodic events and rough time keeping

IRQ0_ISR: 
        cli
        push    eax
        push    ebx
        push    ecx
        push    edx
	push    ds

        mov     ds,cs:_SelData
        mov ebx,play_rate
        add _TimerTicks,ebx
        add  InnerTicks,ebx
        add    SecTicks,ebx
        DoTheSound
        sti
        mov eax,SecTicks
        sub eax,ONESECOND
        jb done_sec
        mov SecTicks,eax
        inc _Seconds
done_sec:        
        
        mov eax,InnerTicks
        sub eax,TIMERTICK
        jb done_irq0
        mov InnerTicks,eax
        ; n.b. TTick conta i ticks di sistema in formato fixed point
        ;      con una cifra decimale
        inc _SysTicks
zapped_time:
        ; it's time to call the old ISR
        mov al,8
        int 33h
        jmp short irq0_digged
done_irq0:        
        mov al,EOI
        out PIC0_CTRL,al
irq0_digged:        
        pop ds
        pop edx
        pop ecx
        pop ebx
        pop eax
        iretd ; ritorna il controllo a 386 power
        
;-----------------------------------------------------------------------------
; 8bit, not compressed VOC to "raw 8 bit pcm"
          align dword
voc_rate  dd ?
play_rate dd ? ; playback sample rate and IRQ0 frequency under PWM sound

          align byte
pwm_table db 1 , 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20
          db 21,21,22,22,23,23,24,24,25,25,26,26,27,27,28,28,29,29,30,30
          db 31,31,31,32,32,32,33,33,33,34,34,34,35,35,35,36,36,36,37,37          
          db 37,38,38,38,39,39,39,40,40,40,41,41,41,42,42,42,43,43,43,44          
          db 45,45,45,46,46,46,47,47,47,48,48,48,49,49,49,50,50,50,51,51
          db 51,52,52,52,53,53,53,54,54,54,55,55,55,56,56,56,57,57,57,57
          db 58,58,58,58,59,59,59,59,60,60,60,60,61,61,61,61,62,62,62,62          
          db 63,63,63,63,63,64,64,64,64,64,65,65,65,65,65,66,66,66,66,66
          db 67,67,67,67,67,68,68,68,68,68,69,69,69,69,69,70,70,70,70,70
          db 71,71,71,71,71,71,71,71,71,71,72,72,72,72,72,72,72,72,72,72
          db 73,73,73,73,73,73,73,73,73,73,74,74,74,74,74,74,74,74,74,74
          db 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75
          db 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75
          
voc_table db 1 , 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20
          db 21,21,22,22,23,23,24,24,25,25,26,26,27,27,28,28,29,29,30,30
          db 31,31,31,32,32,32,33,33,33,34,34,34,35,35,35,36,36,36,37,37          
          db 37,38,38,38,39,39,39,40,40,40,41,41,41,42,42,42,43,43,43,44          
          db 45,45,45,46,46,46,47,47,47,48,48,48,49,49,49,50,50,50,51,51
          db 51,52,52,52,53,53,53,54,54,54,55,55,55,56,56,56,57,57,57,57
          db 58,58,58,58,59,59,59,59,60,60,60,60,61,61,61,61,62,62,62,62          
          db 63,63,63,63,63,64,64,64,64,64,65,65,65,65,65,66,66,66,66,66
          db 67,67,67,67,67,68,68,68,68,68,69,69,69,69,69,70,70,70,70,70
          db 71,71,71,71,71,71,71,71,71,71,72,72,72,72,72,72,72,72,72,72
          db 73,73,73,73,73,73,73,73,73,73,74,74,74,74,74,74,74,74,74,74
          db 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75
          db 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75
          
set_comp macro
           lodsb
           or al,al
           mov al,2 ; compressed data
           jne voc_end
         endm
           
set_sample macro
             lodsb
             and eax,0FFh
             mov ebx,256
             mov edx,0
             mov eax,1000000
             div ebx
             ; eax = samples/sec
             mov ebx,eax
             mov edx,0
             mov eax,1193180
             div ebx
             ; eax = timer count equivalent to sample rate
             mov voc_rate,eax
           endm
           
blitquiet macro
            ; ecx =ctr
            mov edx,0
            mov ebx,voc_rate
            mov ebp,play_rate
            mov eax,0
stamp:
            dec ecx
            je end_sil
            add edx,ebx
            cmp edx,ebp
            jb stamp
            sub edx,ebp
            stosb
            jmp stamp
end_sil:    
          endm
          
blitsound macro
            ; ecx = ctr
            mov edx,0
zound:      mov ebx,0
            mov eax,0
interp:     add al,[esi]
            adc ah,0
            inc esi
            inc ebx
            dec ecx
            je end_data
            add edx,voc_rate
            cmp edx,play_rate
            jb interp
end_data:   sub edx,play_rate
            div bl
            mov al,[eax + voc_table]
            stosb
            cmp ecx,0
            jne zound
            endm
            
          
VocPackets  dd offset voc_end, offset voc_data, offset voc_cont
            dd offset quiet, offset marker, offset ascii
            dd offset rep_start, offset rep_end, offset extend
decoder: mov ebx,0
decode:  lodsd
        mov bl,al
        cmp al,9
        jnb voc_end
        jmp [ebx*4+ VocPackets]

rep_end: mov al,1 ; rep end without rep_start if it is not matched
voc_end: ret

rep_start:
        lodsw
        push esi ; inserisci puntatore
reploop:
        mov esi,[esp]  ; ricarica puntatore
        push eax
        call decode
        pop eax
        dec ax
        jne reploop
        add esp,4 ; rimuovi puntatore        
        jmp decode
        
voc_data:
        mov ecx,-2
        shr eax,8
        add ecx,eax
        set_sample
        set_comp
sblit:
        blitsound
        jmp decoder
        
voc_cont:
        shr eax,8
        mov ecx,eax
        jmp sblit
        
quiet:
        movzx eax, word ptr [esi]
        mov ecx,1
        add esi,2
        add ecx,eax
        set_sample
        blitquiet
        jmp decoder
        
extend: mov al,3 ; extended voc file
        ret        
        
marker:
        add esi,2
        jmp decode
        
ascii:
        shr eax,8
        add esi,eax
        jmp decode
        
vocstamp db 'Creative Voice File',1Ah

        public _Voc2RAW
_Voc2RAW:
        ; in:
        ; esi = .VOC data to translate, edi = destination for RAW data        
        ; al = error code (0 == no errors)
        ; ebx = RAW datablock
        ; edi = end of RAW datablock + 1
        push edi ; save data start
        add edi,4
        movzx ebx,word ptr [esi+14h]
        mov edx,esi
        mov ecx,0
        add esi,ebx        
        mov ebx, offset  vocstamp
votest: mov eax,[edx]
        cmp eax,[ebx]
        jne eend
        add edx,4
        add ebx,4
        inc ecx
        jmp votest
eend:   cmp ecx,5
        jne novoc
        call decoder
        pop ebx ; restore data start
        sub edi,ebx
        sub edi,4
        mov [ebx],edi
        ; al is zero (value transmitted from decoder)
        ret                
novoc:
        add esp,4 ; remove pointer to start of voc file
        mov al,4 ; not a voc file
        ret        

code32  ends

code16 segment para public use16
       assume cs:code16,ds:code32
       ; here comes the 16-bit handlers for irq0 and irq1
       ; (to support bimodal irqs without wasteful callbacks)
       
; RAW KEYBOARD
; 16bit section, Keyboard ISR for raw keyboard input

IRQ1_RISR: ;Real mode ISR
        ; WARNING! BE SURE 386Video IS LINKED IMMEDIATLY AFTER
        ; 386Power (to be sure all the var accessed are into a 64k range)
        ; OR STRANGE THINGS MAY HAPPEN!!!!!!!
        ; (depending on what assembler you use to assemble this stuff)
        push    eax
        push    ebx
        push    ds
        mov     ax,seg code32
        mov     ds,ax
        
	in      al,60h          ; get scan code

        movzx   ebx,al          ; move scan code to index register

	in      al,61h          ; get control code
        push    eax
	or      al,80h          ; clear keyboard of interrupt:
	out     61h,al          ;
        pop     eax             ;   first send control byte with inverted MSB
	out     61h,al          ;   then send plain control byte

        mov     al,EOI          ; send generic EOI to
        
        out     PIC0_CTRL,al    ;   PIC
				; enabling other interrupts
        mov al,bl
        and bl,07Fh ; remove flag bit
	; if the key was released, the high bit is set in the scan code
        mov byte ptr ds:[ebx+offset _RKB],02h ; reset "Is being pressed" flag
                                              ; and left set "Has been pressed"
        
        rol al,1           ;
        jc  rkey_released  ;
        mov byte ptr ds:[ebx+offset _RKB],03h ; set "Is being pressed" flag
                                              ; and "Has been pressed" flag
rkey_released:
        mov ds:_RKBPressed,1     ; state of keyboard has changed
        pop ds
        pop ebx
        pop eax
        iret

; PWM SOUND SPEAKER DRIVER

 rdont_buzz:
        cmp Buzzing,0
        je rgetoutsound
        mov Buzzing,0
        ; turn speaker OFF
        in      al, 61h                 ;Read I/O port B into AL
        and     al, 11111100b           ;mask lower two bits
        out     61h, al                 ;to turn off speaker
        jmp rgetoutsound

RDoTheSound macro
        cmp _SoundOn,0
        je  rdont_buzz    ; no sound if sound flag is off
        cmp _SoundLen,0
        je  rdont_buzz    ; no sound if at end of "soundtrack"
        dec _SoundLen    ; decrease "soundtrack" lenght

        mov     ebx,_SoundPtr    ;get current count pointer
        inc     _SoundPtr        ; increase pointer
        mov     eax,ebx
        shr     ebx,4
        and     ax,0Fh
        add     bx,seg code32
        mov     es,bx
        mov     al,es:[ebx]            ; count into AX

        or      al,al         ; zero count sets speaker off
        ;je      rdont_buzz    ;
        mov     ah,al         ;
        
        mov     al,P_SET2S
        out     PIT_CTRL,al      


        mov     al,ah
        out     PIT2, al             

        cmp Buzzing,0
        jne rgetoutsound
        mov Buzzing,1
        ; turn sound on
        in      al, 61h                 ;read I/O port B into AL
        or      al,3                    ;turn on bits 0 and 1
        out     61h,al                  ;to turn on speaker
rgetoutsound:
        endm
        
; IRQ0 Timer handler, it handles TWO different "time driven" nested loops
; the 8008 Hz PWM sound sytem
; the 18,2 Hz System tick for periodic events and rough time keeping

IRQ0_RISR: 
        push    eax
        push    ebx
        push    ecx
        push    edx
	push    ds
        push    es
        mov     ax,code32
        mov     ds,ax
        mov ebx,play_rate
        add _TimerTicks,ebx
        add  InnerTicks,ebx
        add    SecTicks,ebx
        RDoTheSound
        sti
        mov eax,SecTicks
        sub eax,TIMERTICK
        jb rdone_sec
        mov SecTicks,eax
        inc _Seconds
rdone_sec:        
        
        mov eax,InnerTicks
        sub eax,TIMERTICK
        jb rdone_irq0
        mov InnerTicks,eax
        ; n.b. TTick conta i ticks di sistema in formato fixed point
        ;      con una cifra decimale
        inc _SysTicks
rzapped_time:
        ; it's time to call the old ISR
        pushf
        call dword ptr cs:[(8*4) + offset _OldInt]
        jmp short rirq0_digged
rdone_irq0:        
        mov al,EOI
        out PIC0_CTRL,al
rirq0_digged:    
        pop es    
        pop ds
        pop edx
        pop ecx
        pop ebx
        pop eax
        iret ; ritorna il controllo a 386 power
        
PFLP_RISR: 
        push    eax
        push    ebx
	push    ds
        mov     ax,code32
        mov     ds,ax
        mov ebx,FrameTicks
        add _TimerTicks,ebx
        add  InnerTicks,ebx
        add    SecTicks,ebx
        sti
        mov eax,SecTicks
        sub eax,ONESECOND
        jb prdone_sec
        mov SecTicks,eax
        inc _Seconds
prdone_sec:        
        
        mov eax,InnerTicks
        sub eax,TIMERTICK
        jb prdone_irq0
        mov InnerTicks,eax
        ; n.b. TTick conta i ticks di sistema in formato fixed point
        ;      con una cifra decimale
        inc _SysTicks
przapped_time:
        ; it's time to call the old ISR
        pushf
        call dword ptr cs:[(8*4) + offset _OldInt]
        jmp short prirq0_digged
prdone_irq0:        
        mov al,EOI
        out PIC0_CTRL,al
prirq0_digged:    
        pop ds
        pop ebx
        pop eax
        iret ; ritorna il controllo a 386 power
       
code16 ends

	END
[ RETURN TO DIRECTORY ]