While we're at it, why don't we verify our keystrokes with what actually gets recognized by the computer?

First let's find out in memory where the screen memory actually is:

On the initial load of business basic, there's a directory listing. Let's choose something on screen that's recognizable: "VOLUMES.DOC".

Writing a little lua function to scan the memory, and then a few false starts: screen memory is split into two blocks one at 0x400 and 0x800 with every other character, so VOLUMES.DOC would be VLMSDC or OUE.O, and the high bit needs to be set.

Code
mem=emu.item(manager:machine().devices[":ram"].items["0/m_pointer"])

function scanmatch(byteseq,mem,start)  for addr = start,start+string.len(byteseq)-1 do if mem:read(addr)~=string.byte(byteseq,1+addr-start) then return false end  end return true end

function eachchar(s,func) ret="" for i=1,#s do ret=ret..string.char(func(string.byte(s,i))) end return ret end
function sethibit(achar) return achar | 0x80 end

function hex(a) return string.format("%02x",a) end

for i=0,0x40000 do if scanmatch(eachchar("VLMSDC",sethibit),mem,i) then print(hex(i)) end end

which tells us that the screen memory is at 0x38000 + 0x400 (for page 1) and 0x38000+0x800 (for page 2)

[MAME]> for i=0,0x40000 do if scanmatch(eachchar("VLMSDC",sethibit),mem,i) then print(hex(i)) end end
38b07
[MAME]> for i=0,0x40000 do if scanmatch(eachchar("OUE.O",sethibit),mem,i) then print(hex(i)) end end
38708


Knowing that we can write a little basic program to read a keystroke, print it in the upper left at hpos=1 and vpos =1 which would be address 0x38000+0x400 and we can read that to check against what we're sending:

Code
function myco() 
quitco=nil 
emu.keypost([[
new
90 home
100 get a$:HPOS=1:VPOS=1:? a$"  ASC="ASC(A$)"    "
105 for i = 1 to 300:next i
110 GOTO 100
run
]])
mem=emu.item(manager:machine().devices[":ram"].items["0/m_pointer"])
emu.wait(15) 
for i=32,126 do 
emu.keypost(string.char(i)) 
emu.wait(.5)
screenchar=mem:read(0x40000-0x8000+0x400) 
print("SENT: "..string.char(i).."   SCREEN:"..string.char(screenchar&0x7f)) 
if quitco then return end end end
co1=coroutine.create(myco) coroutine.resume(co1)

and the output looks like:

SENT: SCREEN:
SENT: ! SCREEN:!
SENT: " SCREEN:"
SENT: # SCREEN:#
SENT: $ SCREEN:$
SENT: % SCREEN:%
SENT: & SCREEN:&

Interestingly, afterward, typing CTRL+C to exit the basic program, you have to blind type "TEXT" and enter to get your normal screen back. I think that printing CHR$(3) does something strange to the console driver.

I initially wanted to use the basic KBD function but I couldn't get KBD to work. "PRINT KBD" would always give me zero. GET A$ works just fine. (edit: Oh I see how KBD works from the business basic vol 1 manual, you have to use ON KBD GOTO for KBD to get set properly)

The uppercase and lowercase characters seem to be reversed, there must be a reason for that, it'd be simple to flip the order of the PORT_CHAR to lowercase first, then uppercase character.


Let's also figure out how the addresses at a000 gets mapped into memory:

Code
function hextobytes(h) local a,b,c c="" h=string.upper(h)for i=1,string.len(h),2 do a=string.byte(h,i)-string.byte("0") if a>16 then a=a-7 end b=string.byte(h,i+1)-string.byte("0") if b>16 then b=b-7 end c=c..string.char(a*16+b) end return c end

for i=0,0x40000 do if scanmatch(hextobytes("4ce5a020ca"),mem,i) then print(hex(i)) end end
and it tells us that a000 gets mapped into 3a000.

[Linked Image from i.imgur.com]

Last edited by Golden Child; 08/29/19 04:32 AM.