;X86IOPIX.ASM
.MODEL SMALL ; forces Masm to make a .EXE
.STACK 040h
.CODE
.386

;****************************************************************************
;**                   MODULE EXAMPLE FOR CpcALive                          **
;****************************************************************************
; Assembly from the CpcAlive directory:
; Ml /Zi /c /FoEX\X86IOPix.OBJ EX\X86IOPix.ASM
;	-> create the file EX\X86IOPix.OBJ
; LINK /LINENUMBERS EX\X86IOPix.OBJ,EX\X86IOPix.EXE,EX\X86IOPix.MAP,,,
;	-> create the file EX\X86IOPix.EXE and EX\X86IOPix.MAP
; Loading this module from the Dos command line: ! EX\X86IOPix.EXE
; Loading this module from a CpcAlive command file: EX\X86IOPix.EXE
; Loading this module from the Cpc BASIC interpreter: |INPUT, "EX\X86IOPix":call 0
; commands from the Dos command line:
; |OPIXDBG,X,Y,pen   - FOR DEBUGGER EXAMPLE
; |OPIX,X,Y,pen   - OUTPUT PIXEL(X and Y are relative to the top left corner of the screen)
; |IPIX,X,Y,@pen% - INPUT PIXEL (X and Y are relative to the top left corner of the screen)
; (initialize the variable @pen% with pen%=0 for |IPIX)
; |PALSET,@PAL(0) - SET PALETTE FROM A SUBSCRIPTED VARIABLE
; |PALASK,@PAL(0) - LOAD PALETTE INTO A SUBSCRIPTED VARIABLE
; |PALGREY        - CONVERT A PALETTE INTO SHADES OF GRAY
; from here X and Y are relative to the bottom left corner of the screen
; |BOX,X,Y,WITDTH,HEIGHT,PEN - CLEAR SCREEN BLOCK WITH COLOR (mode 3 only)
; |BOXSTORE,X,Y,WITDTH,HEIGHT - SCREEN BLOCK TRANSFER TO BUFFER(MODE 3 only)
; |BOXREST,X,Y - RAM BLOCK TRANSFERT TO SCREEN with BackGround/ForeGround handling(mode 3 only)
; |BACKGND,PEN - SET BACKGROUND COLOR
; |FOREGND,PEN,TYPE - SET FOREGROUND COLOR
;****************************************************************************

START:          push cs
                pop ds
                mov dx,offset Mes
                mov ah,9
                int 021H
                mov ax,04C00h
                int 021h

Mes             db "Ce fichier est un module CpcAlive.",13,10
                db "This file is a CpcAlive module.",13,10
                db "http://cpcalive.com.",13,10,"$"

;installation table of this X86 module

org 0200h
                db 'X86CPC'			; 0200h mark
                dw 2                ; 0206h version
                dw offset X86Vec    ; 0208h entry vectors table
                dw 0                ; 020Ah reserved
                dw 0                ; 020Ch reserved
                dw 0                ; 020Eh reserved
                dw offset MnemoTb   ; 0210h offset names table for calls with Basic
                db -1               ; 0212h (*) receipt the X86 module area number
                db -1               ; 0213h module complement number or bank Cpc block number
                db -1                             ; 0214h (*) reçeipt the Z80 rom number associated
                db -1               ; 0215h indicator for Cpc banks or modules complements connection
                dw -1               ; 0216h (*) receipt the first Cpc accessible address (-1=no address accessible)
                db 0                ; 0218h reserved
                dw 0                ; 0219h (*) receipt the ems handle associated to the module
                dw 0                ; 021Bh (*) receipt the logical number of the first 16K page of the module
                db 0                ; 021Dh reserved
                dw 0                ; 021Eh address called when the emulator exit (0=no call)
                dw 0                ; 0220h address called when the emulator is put in "sleep mode" (0=no call)
                dw 0                ; 0222h address called when the emulator return from "sleep mode" (0=no call)
                dw 0                ; 0224h address called when the module is load (0=no call)
                dw 0                ; 0226h reserved
                db 0                ; 0228h receipt the number of 16K pages allocated to the module (*)
                                    ;            or number of pages to connect
				dw offset MapName	; 0229h offset .MAP file for debugger
									;		assembly:	ML /Zi /c /FoFileName.OBJ Filename.ASM
									;					LINK /LINENUMBERS FileName.OBJ,FileName.EXE,FileName.MAP,,,
				dw 0				; 022Bh memorize the stack segment SS for C++ modules
				dw 0				; 022Dh memorize the stack pointer SP for C++ modules
				db 6				; 022Fh vector number to debug (-1=not set)
                db 0100h-030h dup (0)   ; 0230h reserved

;(*) = value generated by the system

;*****************************************************************************
MnemoTb         db "PIXIN","I" or 080h          ; |PIXINI
                db "OPI","X" or 080h            ; |OPIX
                db "IPI","X" or 080h            ; |IPIX
                db "PALSE","T" or 080h          ; |PALSET
                db "PALAS","K" or 080h          ; |PALASK
                db "PALGRE","Y" or 080h         ; |PALGREY
                db "OPIXDB","G" or 080h         ; |OPIXDBG <-- Debugger X86 test --
                db "BO","X" or 080h             ; |BOX
                db "BOXSTOR","E" or 080h        ; |BOXSTORE
                db "BOXRES","T" or 080h         ; |BOXREST
				db "BACKGN","D" or 080h			; |BACKGND,PEN
				db "FOREGN","D" or 080h			; |FOREGND,PEN
                db 0

X86Vec          dw offset PixIni        ; 00 ** |PIXINI - INIT **
                dw offset OPix          ; 01 ** |OPIX,X,Y,PEN - OUTPUT PIXEL TROUGHT INT 010H **
                dw offset IPix          ; 02 ** |IPIX,X,Y,@PEN% - READ PIXEL TROUGHT INT 010H **
                dw offset PalSet        ; 03 ** |PALSET,@PAL(0) - SET PALETTE FROM A SUBSCRIPTED VARIABLE **
                dw offset PalAsk        ; 04 ** |PALASK,@PAL(0) - LOAD PALETTE INTO A SUBSCRIPTED VARIABLE **
                dw offset PalGrey       ; 05 ** |PALGREY - CONVERT A PALETTE INTO SHADES OF GRAY **
										; -- X86 test debugger --
										; CS:022Fh=6=vector number to debug (-1=not set)
                dw offset OPixDbg       ; 06 ** |OPIXDBG,X,Y,PEN - OUTPUT PIXEL TROUGHT INT 010H **
                dw offset Box           ; 07 ** |BOX,X,Y,WITDTH,HEIGHT,PEN **
				dw offset BoxStore      ; 08 ** |BOXSTORE,X,Y,WITDTH,HEIGHT **
				dw offset BoxRestore    ; 09 ** |BOXREST,X,Y **
				dw offset BackGndColorSet; 10 ** |BACKGND,PEN - SET BACKGROUND COLOR **
				dw offset ForeGndColorSet; 11 ** SET FOREGROUND COLOR - |FOREGND,PEN **			
                ;... 128 vectors max

MapName			db 'EX\X86IOPix.MAP',0			; .MAP filename for debugger

; AH or AX values for the interruption 018H
; ** Reading the ram BANK 0 of the Cpc (ram blocs 0,1,2,3) from the X86 environment
ReadAL=0        ; (AH) Read the byte pointed by SI and put it in AL
ReadAH=0200h    ; (AX) Read the byte pointed by SI and put it in AH
ReadBL=0201h    ; (AX) Read the byte pointed by SI and put it in BL
ReadBH=0202h    ; (AX) Read the byte pointed by SI and put it in BH
ReadCL=0203h    ; (AX) Read the byte pointed by SI and put it in CL
ReadCH=0204h    ; (AX) Read the byte pointed by SI and put it in CH
ReadDL=0205h    ; (AX) Read the byte pointed by SI and put it in DL
ReadDH=0206h    ; (AX) Read the byte pointed by SI and put it in DH
ReadAX=0207h    ; (AX) Read the word pointed by SI and put it in AX
ReadBX=0208h    ; (AX) Read the word pointed by SI and put it in BX
ReadCX=0209h    ; (AX) Read the word pointed by SI and put it in CX
ReadDX=020Ah    ; (AX) Read the word pointed by SI and put it in DX
ReadSI=020Bh    ; (AX) Read the word pointed by SI and put it in SI
ReadDI=020Ch    ; (AX) Read the word pointed by SI and put it in DI
ReadBP=020Dh    ; (AX) Read the word pointed by SI and put it in BP
; ** Writing in the ram BANK 0 of the Cpc (ram blocs 0,1,2,3) from the X86 environment
WriteAL=1       ; (AH) Write the value of AL in the byte pointed by DI
WriteBL=0301h   ; (AX) Write the value of BL in the byte pointed by DI
WriteBH=0302h   ; (AX) Write the value of BH in the byte pointed by DI
WriteCL=0303h   ; (AX) Write the value of CL in the byte pointed by DI
WriteCH=0304h   ; (AX) Write the value of CH in the byte pointed by DI
WriteDL=0305h   ; (AX) Write the value of DL in the byte pointed by DI
WriteDH=0306h   ; (AX) Write the value of DH in the byte pointed by DI
WriteBX=0308h   ; (AX) Write the value of BX in the word pointed by DI
WriteCX=0309h   ; (AX) Write the value of CX in the word pointed by DI
WriteDX=030Ah   ; (AX) Write the value of DX in the word pointed by DI
WriteBP=030Dh   ; (AX) Write the value of BP in the word pointed by DI

PixIni:
                pusha
                mov dx,offset IniMes
                mov ah,9
                int 021H
                popa
                stc                     ; carry="ok"
                RETF                    ; FAR RETURN

IniMes          db " X86IOPix - 2026",13,10
                db "$"

; -- Debugger X86 test --
; CS:022Fh=vector number to debug (-1=not set)
; ** |OPIXDBG,X,Y,PEN - OUTPUT PIXEL TROUGHT INT 010H **
OPixDbg:		jmp Opix

; ** OUTPUT PIXEL TROUGHT INT 010H - |OPIX,X,Y,PEN **
; < input conditions RamCpc:[SI+0] = PEN number
;                    RamCpc:[SI+2] = y (not adjusted to the BASIC coordinates:origin=top left corner)
;                    RamCpc:[SI+4] = x
OPix:
                mov ah,ReadAL           ; load RamCpc:[SI+0] value in AL
                int 018h                ; pen number in AL
                push ax                 ; save pen number
                add si,2                ; Y coordinate point in RamCpc
                mov ax,ReadDX           ; load value in DX
                int 018h                ; Y coordinate in DX
                add si,2                ; X coordinate point in RamCpc
                mov ax,ReadCX           ; load value in CX
                int 018h                ; X coordinate CX
                pop ax                  ; pen number
                mov ah,0Ch              ; //BIOS//FUNCTION 0CH = OUTPUT PIXEL
                INT 010h                ; CALL BIOS INTERRUPTION 010H
                RETF                    ; FAR RETURN

; ** INPUT PIXEL TROUGHT INT 010H - |IPIX,X,Y,@PEN% **
; < input conditions  RamCpc:[SI+2] = y (not adjusted to the BASIC coordinates:origin=top left corner)
;                     RamCpc:[SI+4] = x
; > output conditions RamCpc:[SI+0] = PEN number

IPix:                
                push bx
                push di
                push si                 ; save pointer address PEN
                add si,2                ; Y coordinate point in RamCpc
                mov ax,ReadDX           ; load value in DX
                int 018h                ; Y coordinate in DX
                add si,2                ; X coordinate point in RamCpc
                mov ax,ReadCX           ; load value in CX
                int 018h                ; X coordinate in CX
                mov ah,0Dh              ; //BIOS//FUNCTION 0DH = INPUT PIXEL
                INT 010h                ; CALL BIOS INTERRUPTION 010H
                pop si                  ; restore pointer address PEN
                push ax                 ; save pen number
                mov ax,ReadDI           ; load PEN adress in DI
                int 018h
                pop bx                  ; restore pen number
                xor bh,bh
                mov ax,WriteBX          ; put BX in RamCpc:[DI+0]
                int 018h
                pop di
                pop bx
                RETF                    ; FAR RETURN

; ** |PALASK,@PAL(0) - LOAD PALETTE INTO A SUBSCRIPTED VARIABLE **
PalAsk:
                pusha
                push es
                mov ax,ReadSI           ; load the address of the 1st element of the array into SI
                int 018h

                push si
                sub si,2
                mov ax,ReadCX           ; load "number of elements in the array + 1" into CX
                int 018h
                dec cx
                and ch,3                ; number of elements in the array 3*256 max
                pop di                  ; point 1st element of the table

                push cx
                mov ax,cx               ; number of elements in the array
                mov cx,3                ; divisor
                xor dx,dx               ; ?
                div cx                  ; /3 = number of colors to load into the buffer
                mov cx,ax               ; result in cx

                push cs
                pop es                  ; segment of color palette
                mov dx,offset Palette   ; offset color palette
                mov bx,0                ; first index
                mov ax,01017h           ; //BIOS//FUNCTION: READ CONTENT MULTIPLE COLOR REGISTERS DAC
                INT 010H                ; CALL BIOS INTERRUPTION 010H
                pop cx                  ; retrieve number of elements from the array

                mov si,offset Palette   ; offset color palette
PalAskCont:     mov al,cs:[si]          ; load collor index
                shl al,2                ; 64>255tones
                mov ah,WriteAL          ; Write the value of AL in the byte pointed to by DI.
                int 018h
                add di,2                ; 2 bytes per element for the destination array
                inc si                  ; 1 byte per element for the source buffer
                loop PalAskCont         ; continue if there are still elements
                pop es
                popa
                RETF                    ; FAR RETURN

Palette         db 256*3 dup (0)        ; color palette buffer (RGB)

; ** |PALSET,@PAL(0) - SET PALETTE FROM A SUBSCRIPTED VARIABLE **
; remark: the table must have been defined by the basic instruction DEFINT
PalSet:
                pusha
                push es
                mov ax,ReadSI           ; load the address of the 1st element of the array into SI
                int 018h

                push si
                sub si,2
                mov ax,ReadCX           ; load "number of elements in the array + 1" into CX
                int 018h
                dec cx
                and ch,3                ; number of elements in the array 3*256 max
                pop si

                push cx
                mov di,offset Palette   ; offset color palette
PalSetCont:     mov ah,ReadAL           ; load RamCpc:[SI+0] value in AL
                int 018h                ; pen number in AL
                shr al,2                ; 255>64tones
                mov cs:[di],al          ; set color number in 'Palette' buffer
                add si,2                ; 2 bytes per element for the source table
                inc di                  ; 1 byte per element for the destination buffer
                loop PalSetCont         ; continue if there are still elements
                pop ax                  ; number of elements in the array
                mov cx,3                ; divisor
                xor dx,dx               ; ?
                div cx                  ; /3 = number of colors to redefine
                mov cx,ax               ; result in cx
                push cs
                pop es                  ; segment of color palette
                mov dx,offset Palette   ; offset of color palette
                mov bx,0                ; first index
                mov ax,01012h           ; //BIOS//FUNCTION: LOAD MULTIPLE COLOR DAC REGISTERS
                INT 010h                ; CALL BIOS INTERRUPTION 010H
                pop es
                popa
                RETF                    ; FAR RETURN


; ** |PALGREY - CONVERT A PALETTE INTO SHADES OF GRAY **
PalGrey:
                pusha
                mov bx,0                ; first index
                mov cx,256              ; number of colors to process
                mov ax,0101Bh           ; //BIOS//FUNCTION: Convert multiple DAC color
                                        ;                   registers to shades of gray
                INT 010h                ; CALL BIOS INTERRUPTION 010H
                popa
                RETF                    ; FAR RETURN


; ** |BOX,X,Y,WITDTH,HEIGHT,PEN - CLEAR SCREEN BLOCK WITH COLOR (mode 3) **
; < input conditions RamCpc:[SI+0] = PEN number
;                    RamCpc:[SI+2] = HEIGHT
;                    RamCpc:[SI+4] = WIDTH
;                    RamCpc:[SI+6] = y
;                    RamCpc:[SI+8] = x
Box:
				xor edi,edi
				mov ax,ReadDI           ; load RamCpc:[SI+0] value in DI
                int 018h                ; pen number in DI
                add si,2                ; HEIGHT in RamCpc
                mov ax,ReadBX           ; load value in BX
                int 018h
				
				push bx					; memorize Height
				shl ebx,16				; high EBX=HEIGHT
                add si,2                ; WIDTH in RamCpc
                mov ax,ReadBX           ; load value in BX
                int 018h				; BX=WIDTH
                add si,2                ; Y coordinate point in RamCpc
                mov ax,ReadDX           ; load value in DX
                int 018h                ; Y coordinate in DX
				
				;adjust to the BASIC coordinates:origin=bottom left corner
                push bx
				mov ax,0F00h    ; ** SCREEN INFOS **
				INT 018h		;>AX=screen height,BX=screen width
				pop bx
				sub ax,dx
				mov dx,ax
				pop ax					; height
				sub dx,ax
				
				add si,2        ; X coordinate point in RamCpc
                mov ax,ReadCX   ; load value in CX
                int 018h        ; X coordinate CX
                mov ax,0F13h    ; ** CLEAR SCREEN BLOCK WITH COLOR (mode 3) **
								;>DX=destination screen line (Y)
								; CX=window start (X) in pixels
								; BX=window width in pixels
								; high EBX= window height in pixels
								; EDI=PEN number				
				INT 018h
				RETF            ; FAR RETURN

; ** |BOXSTORE,X,Y,WITDTH,HEIGHT - SCREEN BLOCK TRANSFER TO BUFFER(MODE 3) **
; < input conditions RamCpc:[SI+0] = HEIGHT
;                    RamCpc:[SI+2] = WIDTH
;                    RamCpc:[SI+4] = y
;                    RamCpc:[SI+6] = x
BoxStore:
                mov ax,ReadBX   ; load RamCpc:[SI+0] value in BX
                int 018h
				mov cs:BoxHeight,bx	; memorize Height
				shl ebx,16		; high EBX=HEIGHT
                add si,2        ; WIDTH in RamCpc
                mov ax,ReadBX   ; load value in BX
                int 018h		; BX=WIDTH
                add si,2        ; Y coordinate point in RamCpc
                mov ax,ReadDX   ; load value in DX
                int 018h        ; Y coordinate in DX

				;adjust to the BASIC coordinates:origin=bottom left corner
                push bx
				mov ax,0F00h    ; ** SCREEN INFOS **
				INT 018h		;>AX=screen height,BX=screen width
				pop bx
				sub ax,dx
				mov dx,ax
				sub dx,cs:BoxHeight

				add si,2        ; X coordinate point in RamCpc
                mov ax,ReadCX   ; load value in CX
                int 018h        ; X coordinate CX

                mov ax,0F10h	; ** SCREEN BLOCK TRANSFER TO BUFFER(MODE 3) **
				INT 018h		; <DX=source screen line (Y)
								;  CX=window start(X) in pixels
								;  BX=window width in pixels
								;  high EBX=window height in pixels
								; >ES:[DI+0]=X source
								;  ES:[DI+2]=Y source
								;  ES:[DI+4]=window width in pixels
								;  ES:[DI+6]=window height in pixels
								;  ES:DI+8 point to start of saved pixel block

				mov word ptr cs:BoxDataPtr,di
				mov word ptr cs:BoxDataPtr+2,es
                RETF            ; FAR RETURN

BoxDataPtr		dd -1
BoxHeight		dw 0	; memorize Height

; ** |BOXREST,X,Y **
; ** RAM BLOCK TRANSFERT TO SCREEN with BackGround/ForeGround handling(mode 3)**
; < input conditions RamCpc:[SI+0] = Y
;                    RamCpc:[SI+2] = X
BoxRestore:
                mov ax,ReadDX   ; load value in DX
                int 018h        ; Y coordinate in DX
				;adjust to the BASIC coordinates:origin=bottom left corner
                push bx
				mov ax,0F00h    ; ** SCREEN INFOS **
				INT 018h		;>AX=screen height,BX=screen width
				pop bx
				sub ax,dx
				mov dx,ax
				sub dx,cs:BoxHeight
				
				add si,2        ; X coordinate point in RamCpc
                mov ax,ReadCX   ; load value in CX
                int 018h        ; X coordinate CX

				mov si,word ptr cs:BoxDataPtr
				mov ds,word ptr cs:BoxDataPtr+2
                mov ax,0F12h    ; ** RAM BLOCK TRANSFERT TO SCREEN with BackGround/ForeGround handling(mode 3)**
				INT 018h		;<DS:SI point to source buffer
								; DX=destination screen line (Y)
								; CX=window start (X) in pixels
								; DS:[SI+4]=window width in pixels
								; DS:[SI+6]=window height in pixels
								; DS:SI+8 point the block of pixels
				RETF            ; FAR RETURN

; ** |BACKGND,PEN - SET BACKGROUND COLOR **
; < input conditions RamCpc:[SI+0] = PEN number(-1=not set)
BackGndColorSet:mov ebx,-1
				mov ax,ReadBX	; load RamCpc:[SI+0] value in BX
                int 018h
                mov ax,0F01h    ; ** SET BACKGROUND COLOR **
				INT 018h		;<EBX=Background color	-1=not set
				RETF            ; FAR RETURN

; ** |FOREGND,PEN,TYPE - SET FOREGROUND COLOR **
; < input conditions	RamCpc:[SI+0] = Type of treatment
;						RamCpc:[SI+2] = PEN number(-1=not set)
;The different types of treatment are:
;0=The pixel is displayed if its value is higher than the screen pixel and the 'FOREGROUND COLOR' value
;1=A XOR operation is performed between the value of the pixel to be displayed and the pixel on the screen if its value is greater than the 'FOREGROUND COLOR' value
;2=An OR operation is performed between the value of the pixel to be displayed and the pixel on the screen if its value is greater than the 'FOREGROUND COLOR' value
;3=An AND operation is performed between the value of the pixel to be displayed and the pixel on the screen if its value is greater than the 'FOREGROUND COLOR' value

ForeGndColorSet:mov ax,ReadDX	; load RamCpc:[SI+0] value in DX
                int 018h
				cmp dx,-1
				mov ebx,-1
				jz short ForeGndColorReset
                add si,2
				mov ax,ReadBX	; load RamCpc:[SI+2] value in BX
                int 018h
ForeGndColorReset:
				mov ax,0F02h    ; ** SET FOREGROUND COLOR **
				INT 018h		;<EBX=Foreground color	-1=not set
								; DX=Type of treatment
				RETF            ; FAR RETURN

end start
