'===========================================================================
' Subject: READING CHARACTER OFF SCREEN       Date: 03-01-97 (12:28)       
'  Author: The ABC Programmer                 Code: QB, QBasic, PDS        
'  Origin: voxel@freenet.edmonton.ab.ca     Packet: FAQS.ABC
'===========================================================================
' Question:
'   How can I read a character and attribute off the screen (text), and can
'   you explain how it works?

' Answer:
'   The bytes are stored in memory, so to access them, you must first
'   know where the screen memory address is (where h = hex):
'     b800h:0000 is where we should start looking.
'     b000h:0000 if you have a monochrome card/adapter.
'
'   BASIC equivalent is: DEF SEG = &HB800

DEFINT A-Z
DEF SEG = &HB800  ' &HB000 for mono (&H = Hex)

' Okay, we know where the information is stored... so now we have to
' get them.  For BASIC, that command is PEEK (look it up in HELP).

' For one thing, we know that there are 2,000 possible locations on an 80x25
' text screen.  It's given that each location occupies one word (or two
' bytes).  The first byte is the ASCII value of the character on the virtual
' screen, and the following is the attribute (colour?) of the character.
' I say colour? because it's not a definitive colour, it's actually the
' foreground and background colour kind of combined if you like to look at
' it that way... I'll go into how to decode the attribute later on.

' so let's see:  b800h:0000 is a character
'                b800h:0001 would be the attribute... and so on.

' Now with some fine tuning, we can come up with an algorithm to find
' the character and the attribute.

' Since there are 160 bytes per row (80 words = 2 bytes)
' - we can say that (Row% - 1) * 160
'     the second row starts at 160, then the next at 320... and so on.
' - and so the column is (Col% - 1) * 2
'     multiply by 2 because there are 2 bytes per location, and we want
'     the character which is every even number.
' - That means (Col% - 1) * 2 + 1 would give us the attribute.
' - Minus 1 because the segment starts at 0000 but QB's row, column starts
'     at (1,1)

'  Putting this all together, we get the complete equation:
'
'  CharAddress = (Row% - 1) * 160 + (Col% - 1) * 2
'  AttrAddress = (Row% - 1) * 160 + (Col% - 1) * 2 + 1
'
' Using (Row%, Col%)
' The top left corner is (1,1) and the bottom right is (25,80)

'------------------------ DEMO1 -----------------------------
Row% = 1       ' Let's read the upper left corner and return its character
Col% = 1       ' and attribute if you want to as well.

CLS
COLOR 7, 0
LOCATE Row%, Col%: PRINT "Hello"

Char = PEEK((Row% - 1) * 160 + (Col% - 1) * 2)     ' Get first byte (even)
Attr = PEEK((Row% - 1) * 160 + (Col% - 1) * 2 + 1) ' Get next byte (odd)

' Char is an ASCII value, so if you wanted the string equivalent:

Char$ = CHR$(Char)

PRINT "Character at (1,1) is: "; Char$

ForeGround = Attr MOD 16
BackGround = (Attr - ForeGround) / 16

' How does the algorithm above work?
' Because only one byte is stored, the computer encodes the foreground
' and background colour into just one byte... so to undo what the computer
' has done we must first know what the heck it has done:
'   Computer:  Foreground = 12  BackGround = 3
'              So 3 * 16 + 12 = 60
'
'              Why 16?  Because (0,1,2,3,...,15) = 16 different colours!
'
'   Intelligent human being:  Okay, now I'll reverse the evil procedure!
'       ForeGround = 60 / 16 = 3 with Remainder 12
'       But luckily we have the function MOD (look it up in HELP)
'       So 60 MOD 16 will yield the remainder we want, which it 12.
'
'       Therefore, with some work: 60 - 12 = 3 * 16     (Learn your math)
'                                 (60 - 12) / 16 = 3
'
'   So BackGround = (60 - ForeGround) / 16     = 3
'   or BackGround = (60 - ForeGround) MOD 15   = 3

PRINT "Attribute (Colour): "; ForeGround; ","; BackGround

' Let's try another one...
'------------------------ DEMO2 -----------------------------

Row% = 7      ' Select new locations
Col% = 10

COLOR 15, 5
LOCATE Row%, Col%: PRINT "Bye!"

Char$ = CHR$(PEEK((Row% - 1) * 160 + (Col% - 1) * 2))  ' One step process
Attr = PEEK((Row% - 1) * 160 + (Col% - 1) * 2 + 1)

COLOR , 0
PRINT "Character at (7,10) is: "; Char$

ForeGround = Attr MOD 16
BackGround = (Attr - ForeGround) MOD 15

PRINT "Attribute (Colour): "; ForeGround; ","; BackGround

END

' William Yu (voxel@freenet.edmonton.ab.ca)
' 03-01-97
