+; Leave in default mode 3
+; Graphics controller Miscellaneous Graphics Register (Index 06h) = 0x0E
+; Output known data @B8000
+; Set Graphics controller Miscellaneous Graphics Register (Index 06h) = 0x0C (turn off bit 1 Chain Odd/Even Enable)
+; Read from memory and try to find the known data
+; http://www.columbia.edu/~em36/wpdos/videomodes.txt
+; http://www.brokenthorn.com/Resources/OSDevVid2.html
+intromessage: db 'Test Graphics controller (Index 06h) Chain Odd/Even Enable bit', 13, 10
+ db 'Press any key to continue'
+knowndata: db 'a',0x17,'b',0x17,'c',0x17,'d',0x17,'e',0x19,'f',0x19,'g',0x19,'h',0x19,'i',0x1c,'j',0x1c,'k',0x1c,'l',0x1c,'m',0x1e,'n',0x1e
+ db 'o',0x1e,'p',0x1e,'q',0x1a,'r',0x1a,'s',0x1a,'t',0x1a,'u',0x18,'v',0x18,'w',0x18,'x',0x18,'y',0x10,'z',0x10,'!',0x10,' ',0x10
+knowndataLen: equ $ - knowndata ; determine data length
+msgBuffer1Equal: db 'Success, buffer1 matched the known data'
+msgBuffer1Unequal: db 'Error, buffer1 did not match the known data'
+msgBuffer2Equal: db 'Success, buffer2 matched the known data'
+msgBuffer2Unequal: db 'Error, buffer2 did not match the known data'
+msgInitialMode: db 'Initial video mode: 0x' ; string to output
+valInitialMode: dw 0x0000 ; placeholder for 2 byte hex value
+buffer1: resb 56 ; reserve 28 * 2 bytes
+buffer2: resb 0x8000 ; reserve 32Kb
+; save initial video mode
+mov ax,0x0f00 ; ah=0f: VIDEO - GET CURRENT VIDEO MODE
+int 0x10 ; returns: AL = display mode
+; print "Initial video mode:"
+call ConvertByteToHex ; convert al into hex value @ DS:[BX]
+; each row is 160 bytes (even byte = character, odd byte = attribute)
+mov di, 0 ; destination: ES:DI
+mov si, knowndata ; source: DS:SI
+; read memory into buffer1 (56 bytes)
+push ds ; save data segment!!
+mov di, buffer1 ; destination: ES:DI
+mov si, 0 ; source: DS:SI
+pop ds ; restore our data segment
+; Disable Chain Odd/Even
+; Clear bit 1 of Graphics controller Miscellaneous Graphics Register (Index 06h)
+and al, 0xfd ; clear bit 1
+ ;mov ax, 0x0005 ; set index 5 (Shift256/Shift Reg/Host O/E/Read Mode/Write Mode) to 0
+ ;;mov al, 0x0f ; this selects the planes to read from when Seqencer bit 3 index 4 is clear
+ ;;mov al, 0x03 ; this selects the planes to read from when Seqencer bit 3 index 4 is clear
+; Sequencer: Disable Chain 4/Disable Odd/Even Host Memory Write Adressing
+; Clear bit 3, Set bits 2 and 1 of Sequencer Memory Mode Register (Index 04h)
+ mov al, 0x04 ; Graphics controller index 4 (Read Map Select)
+ mov al, 0x00 ; 0x00 = read from plane 0!!
+; read memory into buffer2 (56 bytes)
+push ds ; save data segment!!
+mov di, buffer2 ; destination: ES:DI
+mov si, 0 ; source: DS:SI
+pop ds ; restore our data segment
+; restore the inital video mode
+mov ah, 0x00 ; ah=00: VIDEO - SET VIDEO MODE
+; compare buffer1 with knowndata
+mov si, knowndata ; ds:si points to first string
+mov di, buffer1 ; ds:di points to second string
+mov dx,msgBuffer1Unequal
+; compare buffer2 with knowndata
+mov si, knowndata ; ds:si points to first string
+mov di, buffer2 ; ds:di points to second string
+mov dx,msgBuffer2Unequal
+; print address (just cx for now)
+call ConvertByteToHex ; convert al into hex value @ DS:[BX]
+; output 256 bytes @ ds:si
+cmp al, 'x' ; allow user to terminate
+jz terminate ; in case 'x' is pressed
+msgBufferIndex: db 'Dumping buffer address 0x' ; string to output
+valBufferIndex: dw 0x0000 ; placeholder for 2 byte hex value
+ db '00 (press "x" to exit)',13,10,'$' ; newline
+; Compare strings @DS:SI, ES:DI (max string length to compare specified in cx)
+; AX: 0 if strings are equal, else 1
+; BX: Number of equal characters found
+ lodsb ; load al with char from string 1
+ ; note: lodsb increments si automatically
+ cmp bx, cx ; did we reach the end of the string?
+ mov ax, 0 ; strings were equal
+ jmp end_compare_strings
+ mov ax, 1 ; strings were not equal
+; Output string as hexadecimal values
+; We use bios call rather than DOS string functions
+; bp+8: string (segment)
+; bp-4: highlight colour
+ lodsb ; load al with char from DS:SI
+ ; determine colour (highlight values from 'a' to 'z')
+ jl .pr1 ; jump if less than
+ jg .pr1 ; jump if greater than
+ call ConvertByteToHex ; convert al into hex value
+ mov dl, cl ; if (cx % 16 == 0)
+ mov al, 0x0d ; print a newline character every 16 bytes
+_PrintChar: ;Procedure to print character on screen
+ ;Assume that ASCII value is in register AL
+MOV AH, 0x0E ;Tell BIOS that we need to print one charater on screen.
+MOV BL, 0x07 ;Text attribute 0x07 is lightgrey font on black background
+INT 0x10 ;Call video interrupt
+RET ;Return to calling procedure
+_SpPrintChar: ;Procedure to print character on screen
+ ;Assume that ASCII value is in register AL
+MOV AH, 0x09 ;Tell BIOS that we need to print one charater on screen.
+;;MOV BL, 0x07 ;Text attribute set by caller
+INT 0x10 ;Call video interrupt
+; advance the cursor because BIOS doesn't do it
+RET ;Return to calling procedure
+; Convert btye in AL to 2 digit Hexadecimal string
+; Result returned at WORD address referenced by DS:[BX]
+ db '0123456789ABCDEF', 0
+ lea bx, [ByteToHexTable]
+ mov ah, al ;make al and ah equal so we can isolate each half of the byte
+ shr ah, 4 ;ah now has the high nibble
+ and al, 0x0F ;al now has the low nibble
+ xlat ;lookup al's contents in our table
+ xchg ah, al ;flip around the bytes so now we can get the higher nibble
+ xlat ;look up what we just flipped
+RET ;Return to calling procedure