IO.FLINE doubts and emulation

Anything QL Software or Programming Related.
User avatar
chernandezba
Chuggy Microdrive
Posts: 54
Joined: Thu Dec 15, 2016 7:10 pm

IO.FLINE doubts and emulation

Post by chernandezba »

Hi

I'm the author of the ZEsarUX emulator, which has experimental QL support.
First of all, I don't emulate microdrive hardware. Instead, I trap some QL rom calls and I emulate them by reading files from disk. For example, I can do a:
LBYTES mdv1_examplefile,address
Where this "examplefile" is a real file located on my PC.

So, LBYTES works but not LOAD (or LRUN) basic programs
I have been debugging and when loading from basic uses the call "IO.FLINE" (and loading by lbytes uses FS.LOAD).
Here comes my problems...
According to the QL Technical Guide manual, that call:

Input parameters:
D2.W length of buffer
D3.W timeout
A0 channel lD
A1 base of buffer

Output parameters:
D1.W nr. of bytes fetched
A1 updated ptr to buffer

Possible errors:
NC not complete
NO channel not open
EF end of file
B0 buffer overflow (fetch line only)


So, my doubts are with input A1 and output A1, and where to put the file read data. I tried with a simple example with "1 REMark"+ascii code 10, returning 9 as the data lenght, but I don't know where I have to write those bytes... I tried writing at A1, but A1 seems to have value 100H, and it's not a RAM area... Reading docs some people says that the destination address is A6+A1, which is 28100H (ram space) but it doesn't seem to work. Seems that "base of buffer" is not the real pointer...


So, is there anyone that could explain me:
-Where is the input destination buffer, so I could write my "1 REMark" example. When it works, it will load the real file, of course!
-What are the output registers? specially A1

I will be happy if someone help me with this, I have spent today 6 hours (I'm not joking) trying to complete this :( If I succeed with this "LOAD" basic command sentence, my emulator will be more complete (I can only load by LBYTES)

Thanks!! :)

Cesar


----

ZEsarUX
ZX Second-Emulator And Released for UniX
https://github.com/chernandezba/zesarux
User avatar
tofro
Font of All Knowledge
Posts: 2685
Joined: Sun Feb 13, 2011 10:53 pm
Location: SW Germany

Re: IO.FLINE doubts and emulation

Post by tofro »

Hmmm, where to start?
  1. The QL has, unlike the other Sinclair computers, no fixed address in memory for the BASIC program, it has, in fact, not even a fixed space where the BASIC interpreter runs. The interpreter is one of many jobs that float about the QL's memory. Unlike the other jobs, it can even be moved by the system while it is running.
  2. The QL does, also unlike all other Sinclair computers, not store a BASIC program in tokenized form on disk - A basic program is stored as ASCII. But just like all other Sinclair computers, once the program is loaded to memory, it is stored there in tokenized form. So you can't simply load a program directly into the area where the program space currently is located - It must be tokenized first, and this is done by the system during LOAD and MERGE operations - The program is read in line by line to a temporary buffer, then converted into tokens and moved to the program space.
  3. Because the interpreter's data can be moved about at any time by the operating system, absolute addressing of important data areas is simply not possible. The Interpreter helps itself by using a base register (a6) to point to the base of the Interpreter job and makes all addresses relative to this base pointer - When the system moves the interpreter job around, it simply adjusts this base pointer to the new area, and because everything is expressed with distances to this base pointer, everything is fine.
  4. Because of this complicated floating BASIC areas, the system cannot simply hand an absolute pointer to any system call that wants one (like the buffer address for IO.FLINE). QDOS works around this by introducing TRAP #4, which is basically a switch - The next system call after a TRAP #4 is instructed to interpret anything that is a pointer as relative to a6. Let's assume a6 is $30000 and an IO.FLINE trap is issued without a TRAP #4 before, with A1 containing $20000, the system will use $20000 as a target load address. If, however, a TRAP #4 has been issued before an IO.FLINE call, the system will load to (a6, a1), thus at absolute address $50000. Thus, pointers in traps have to be interpreted according to this switch (which is per-job and re-set after the next TRAP #1, 2, or 3).
  5. BTW $28100 (a1+a6) in your example doesn't look like a reasonable load address for a BASIC program - The QL's system variables start at $28000 and occupy an area of $480 bytes - $28100 is in the middle of the system variables, those should never be overwritten by something loaded from disk. Can you re-check that? $28100 is in fact the first pointer to the file system physical definition block (a data area of the driver)
  6. I would assume a BASIC program would be loaded line-wise into the BASIC buffer area (some sort of scratchpad memory) to be tokenized there - That would live at (($28010)+0) (double indirect pointer, ($28010) points to the SuperBASIC base area, the first long there points to the basic buffer). Don't count on any of these addresses, they will only have these values directly after a reset with no other job running
So, short answer to your question after all this explanation:

If the very same job that calls the IO.FLINE trap has not issued a TRAP #4 directly before, a1 is the absolute target load address, and will be updated after the call to the end of used buffer area.
If, however, the last TRAP issued by the job in question directly before the IO.FLINE trap was a TRAP #4, the address is (a6,a1), and a1 will be updated relatively (i.e. only incremented by the amount of bytes read).

Hope this helps,
Tobias
Last edited by tofro on Fri Dec 15, 2017 11:29 pm, edited 1 time in total.


ʎɐqǝ ɯoɹɟ ǝq oʇ ƃuᴉoƃ ʇou sᴉ pɹɐoqʎǝʞ ʇxǝu ʎɯ 'ɹɐǝp ɥO
User avatar
XorA
Site Admin
Posts: 1359
Joined: Thu Jun 02, 2011 11:31 am
Location: Shotts, North Lanarkshire, Scotland, UK

Re: IO.FLINE doubts and emulation

Post by XorA »

Ill point out the uQlx does the same trapping thing for drive access, so you should be able to get info there.


User avatar
chernandezba
Chuggy Microdrive
Posts: 54
Joined: Thu Dec 15, 2016 7:10 pm

Re: IO.FLINE doubts and emulation

Post by chernandezba »

Thanks
But the problem here is I have tried both combinations:
-a1+a6
And
-just a1

But the two seem to fail :(
Also,What would be the a1 return value?

About tokeniser... I think it is done by another Rom call, so my only function here in this QL call is reading in ascii format the basic program, like the simple “rem” example


----

ZEsarUX
ZX Second-Emulator And Released for UniX
https://github.com/chernandezba/zesarux
User avatar
tofro
Font of All Knowledge
Posts: 2685
Joined: Sun Feb 13, 2011 10:53 pm
Location: SW Germany

Re: IO.FLINE doubts and emulation

Post by tofro »

As I said, what you seem to assume as a load address (a1+a6) - Must be wrong. That is not a valid address for a load.

Tobias


ʎɐqǝ ɯoɹɟ ǝq oʇ ƃuᴉoƃ ʇou sᴉ pɹɐoqʎǝʞ ʇxǝu ʎɯ 'ɹɐǝp ɥO
User avatar
pjw
QL Wafer Drive
Posts: 1286
Joined: Fri Jul 11, 2014 8:44 am
Location: Norway
Contact:

Re: IO.FLINE doubts and emulation

Post by pjw »

I dont quite know where youre at. If you managed to trap and implement iof.load, then iob.flin cant that much harder. The BASIC command LOAD takes care of where each line of bytes are to be loaded. Your routine will, presumably be on the "inside" of the trap, so the address, a1, and the buffer length, d2, will be provided. So all you need to do is supply those bytes terminated by a LF (or a full buffer) and uodate the pointer accordingly. Your trap emulation may need go out of its way to check the jcb_rela flag in the job header to see whether a6 needs to be added. I dont know offhand how this mechanism works, but the SMSQ/E sources should give a clue.


Per
dont be happy. worry
- ?
User avatar
janbredenbeek
Super Gold Card
Posts: 629
Joined: Wed Jan 21, 2015 4:54 pm
Location: Hilversum, The Netherlands

Re: IO.FLINE doubts and emulation

Post by janbredenbeek »

(... excellent explanation of SB floating data areas skipped...)
tofro wrote: [*]BTW $28100 (a1+a6) in your example doesn't look like a reasonable load address for a BASIC program - The QL's system variables start at $28000 and occupy an area of $480 bytes - $28100 is in the middle of the system variables, those should never be overwritten by something loaded from disk. Can you re-check that? $28100 is in fact the first pointer to the file system physical definition block (a data area of the driver)
In S*BASIC, A6 points to the start of the data area of the S*BASIC job, not the QDOS/SMS system variables. If A1 is $100, then (A6,A1.L) points to offset $100 in S*BASICs data area (which is usually the start of the buffer, which explains everything).
[*]I would assume a BASIC program would be loaded line-wise into the BASIC buffer area (some sort of scratchpad memory) to be tokenized there - That would live at (($28010)+0) (double indirect pointer, ($28010) points to the SuperBASIC base area, the first long there points to the basic buffer). Don't count on any of these addresses, they will only have these values directly after a reset with no other job running
On plain old QDOS, $28010 (SV.BASIC) indeed points to the start of SuperBASIC's data area (equal to A6). However in Minerva and SMSQ/E this is no longer true, since there may be more than one S*BASIC job and each job has its own A6 pointer! Even when there is only one S*BASIC running, $28010 no longer points to the same location as S*BASIC's A6. Also, there seems to be a gap of $180 bytes between an SBASIC job header and its A6 pointer, so you should not make any assumption that A6 points right after the job header!

regards, Jan.


User avatar
tofro
Font of All Knowledge
Posts: 2685
Joined: Sun Feb 13, 2011 10:53 pm
Location: SW Germany

Re: IO.FLINE doubts and emulation

Post by tofro »

janbredenbeek wrote: In S*BASIC, A6 points to the start of the data area of the S*BASIC job, not the QDOS/SMS system variables. If A1 is $100, then (A6,A1.L) points to offset $100 in S*BASICs data area (which is usually the start of the buffer, which explains everything).
regards, Jan.
That's exactly right - but (and that's what I was meaning to say): (a6,a1.l) should never be $28100 in a SuperBASIC context, which hints that Cesar was not observing a SuperBASIC context.

Tobias


ʎɐqǝ ɯoɹɟ ǝq oʇ ƃuᴉoƃ ʇou sᴉ pɹɐoqʎǝʞ ʇxǝu ʎɯ 'ɹɐǝp ɥO
User avatar
M68008
Trump Card
Posts: 223
Joined: Sat Jan 29, 2011 1:55 am
Contact:

Re: IO.FLINE doubts and emulation

Post by M68008 »

Depends on where you intercept the call (LBYTES, TRAP#3, device driver I/O routine, or some other place)... If you intercept at the TRAP or device driver I/O routine, A1 should be a valid RAM address. If not, you may have a bug somewhere else.


User avatar
chernandezba
Chuggy Microdrive
Posts: 54
Joined: Thu Dec 15, 2016 7:10 pm

Re: IO.FLINE doubts and emulation

Post by chernandezba »

Thanks everyone for the help! :)
Using the A1+A6 pointer, I succeeded to show a correct md label when using FS.MDINF, so doing a "dir mdv1_" now shows my label
But unfortunately, the "load mdv1_program_bas" doesn't work yet, it returns a "not found" (I'm returning a ok from my trap) and the basic listing is not there.
After calling IO.FLINE for the first line of my example, then it runs the allocate basic space call, and then it opens the device "mdv", which is not "mdv1_" nor "mdv1_program_bas", so it's a bit strange. I tried trapping it and returning a "ok" or even a "not found" but it doesn't work,

The following are the debug messages generated from my emulator beginning at my load sentence and finishing at the "not found" prompt error:

Paranoid: Trap 1. D0=16H D1=200H A0=00000000H A1=00004E2AH A6=0003F068H PC=0031EH is :
Paranoid: Trap 1: MT.ALBAS allocate BASIC area
Paranoid: Trap 1. D0=01H A0=00000F80H A1=00000F80H PC=00324H is :
Paranoid: Trap 1. IO.OPEN
Paranoid: Lenght channel name: 18
Paranoid: Channel name: mdv1_spacepods_bas
Paranoid: Returning from trap without opening anything because file is mdv1, mdv2 or flp1
Debug: Source path: mdv1_spacepods_bas Device: mdv1 File: spacepods.bas
Paranoid: Trap 1. D0=10H D1=FFFFFFFFH A0=00000020H A1=00000F94H A6=0003EE68H PC=0031EH is :
Paranoid: Trap 1: MT.DMODE
Paranoid: Trap 1. D0=10H D1=08H A0=00000020H A1=00000F94H A6=0003EE68H PC=0031EH is :
Paranoid: Trap 1: MT.DMODE
Paranoid: Trap 1. D0=17H D1=400H A0=00000D94H A1=00001194H A6=0003EE68H PC=0031EH is :
Paranoid: Trap 1: MT.REBAS release BASIC area
Paranoid: Trap 3. D0=02H A0=00000020H A1=00000100H A6=0003F268H PC=0032AH is :
Paranoid: Trap 3: IO.FLINE. fetch a line of bytes
Paranoid: IO.FLINE. Channel ID=32 Base of buffer A1=00000100H A3=0003FFF0H A6=00028000H
Paranoid: Returning IO.FLINE from our microdrive channel without error
Paranoid: IO.FLINE - restoreg registers. Channel ID=32 Base of buffer A1=00000100H A3=0003FFF0H A6=0003F268H
Writing IO.FLINE data to 00028100H
Paranoid: Trap 1. D0=16H D1=200H A0=00000020H A1=00004E2AH A6=0003F268H PC=0031EH is :
Paranoid: Trap 1: MT.ALBAS allocate BASIC area
Paranoid: Trap 1. D0=01H A0=00000D8EH A1=00000D8EH PC=00324H is :
Paranoid: Trap 1. IO.OPEN
Paranoid: Lenght channel name: 3
Paranoid: Channel name: mdv
Paranoid: Trap 3. D0=07H A0=00000000H A1=0000B7BAH A6=0003F068H PC=0032AH is :
Paranoid: Trap 3: IO.SSTRG
Paranoid: Trap 1. D0=02H A0=00000020H A1=00000D94H PC=00324H is :
Paranoid: Trap 1. IO.CLOSE
Paranoid: IO.CLOSE. Channel ID=32
Paranoid: Returning IO.CLOSE from our microdrive channel without error
Paranoid: Trap 3. D0=04H A0=00000000H A1=00000100H A6=0003F068H PC=0032AH is :
Paranoid: Trap 3: IO.EDLIN

Thanks!
Cesar


----

ZEsarUX
ZX Second-Emulator And Released for UniX
https://github.com/chernandezba/zesarux
Post Reply