Talking to the IPC: How do you do it from low level?

Anything QL Software or Programming Related.
stephen_usher
Super Gold Card
Posts: 524
Joined: Tue Mar 11, 2014 8:00 pm
Location: Oxford, UK.
Contact:

Talking to the IPC: How do you do it from low level?

Post by stephen_usher »

As you may have noticed, I'm trying to write a bare metal diagnostic ROM and as such I need to work out how to communicate with the IPC via the ZX8302 at the lowest level without any operating system support.

I see that 0x18003 is "IPC write" and I can read if the transmission to the IPC has been completed using the bit in the status register, 0x18020 but how do you read anything from the IPC?

This information seems fragmentary at best, unless I've missed some sort of reference manual for this.

How, has anyone tried this and if so I could do with a bit of a tutorial. :-)


Derek_Stewart
Font of All Knowledge
Posts: 4654
Joined: Mon Dec 20, 2010 11:40 am
Location: Sunny Runcorn, Cheshire, UK

Re: Talking to the IPC: How do you do it from low level?

Post by Derek_Stewart »

Hi,

have you looked at thus message thread:

viewtopic.php?t=2299


Regards,

Derek
stephen_usher
Super Gold Card
Posts: 524
Joined: Tue Mar 11, 2014 8:00 pm
Location: Oxford, UK.
Contact:

Re: Talking to the IPC: How do you do it from low level?

Post by stephen_usher »

Derek_Stewart wrote: Sun Apr 27, 2025 7:45 pm Hi,

have you looked at thus message thread:

viewtopic.php?t=2299
I'm afraid that's not really clarified anything. I've been looking at the Minerva source as well and being a bear of little brain it's not exactly helped!

Some sort of pseudo-code would be helpful for the process, as in something like:

Code: Select all

Write:
Create a command, as per the QDOS documentation.
Send value here to this address -> xxxxx
Check that it's written using this register yyyyy
repeat until all of it's gone.

Read:
Check this register yyyyy
Read from address zzzzz
That sort of level is what I need.


User avatar
Pr0f
QL Wafer Drive
Posts: 1548
Joined: Thu Oct 12, 2017 9:54 am

Re: Talking to the IPC: How do you do it from low level?

Post by Pr0f »

It's only one bit that really carries any data - and another that's used to signal when comms is ready.

From that other thread - I put up the basic sequence for the really low level side:

75 Send bit to IPC
76 ---------------
77
78 1. ZX start bit (COMDATA = 0)
79 2. IPC clock (COMCTL = 0, COMTL = 1)
80 3. ZX data bit (COMDATA = 0/1)
81 4. IPC clock (COMCTL = 0, COMTL = 1)
82 5. ZX stop bit (COMDATA = 1)
83
84 Receive bit from IPC
85 --------------------
86
87 1. ZX start bit (COMDATA = 0)
88 2. IPC clock (COMCTL = 0, COMTL = 1)
89 3. IPC data bit (COMDATA = 0/1)
90 4. IPC clock (COMCTL = 0, COMTL = 1)
91 5. IPC stop bit (COMDATA = 1)

The IPC is expecting a 4 bit command - which then vectors off to the routine you've called in the IPC where it will collect the rest of any data nibbles or bytes that are expected.

The actual IPC code:
; get IPCOM cmd or else scan keyboard
0200: 0A IN A,P2
0201: 53 80 ANL A,#80
0203: C6 07 JZ #0207 comdata=L?
0205: A4 00 JMP $500 scan keyboard
0207: F4 4F CALL $74F get 4bit data from ZX8302, ret A.lsb
0209: 03 0C ADD A,#0C
020B: B3 JMPP @A jump according IPCOM table $20C

And the commands jump table in the program:

; IPCOM jump table
020C: 1C DFB #1C 0: init IPC
020D: 28 DFB #28 1: get interrupt status
020E: 1E DFB #1E 2: open ser1
020F: 1E DFB #1E 3: open ser2
0210: 20 DFB #20 4: close ser1
0211: 20 DFB #20 5: close ser2
0212: 89 DFB #89 6: serial1 receive
0213: 89 DFB #89 7: serial2 receive
0214: 4D DFB #4D 8: read keyboard
0215: 76 DFB #76 9: keyrow
0216: 22 DFB #22 a: set sound
0217: 24 DFB #24 b: kill sound
0218: C3 DFB #C3 c: set P2.3
0219: CF DFB #CF d: set serial baudrate
021A: E3 DFB #E3 e: get random
021B: F0 DFB #F0 f: test
...
the respective routines sit after this table, and will collect additional nibbles or bytes as they require.

Most of the donkey work on the QL side is done by MT.IPCOM or Minerva's MT.IPCMD which assembles and drives the communications from the QL side - so effectively it's that you would need to replicate.


stephen_usher
Super Gold Card
Posts: 524
Joined: Tue Mar 11, 2014 8:00 pm
Location: Oxford, UK.
Contact:

Re: Talking to the IPC: How do you do it from low level?

Post by stephen_usher »

Thanks Pr0f.

Unfortunately I'm being a bit thick and I can't really make head not tail of that.


User avatar
Pr0f
QL Wafer Drive
Posts: 1548
Joined: Thu Oct 12, 2017 9:54 am

Re: Talking to the IPC: How do you do it from low level?

Post by Pr0f »

This may help a bit futher - from Nasta's write up on the ZX8302:


$18003 WRITE IPC Write:

Controls the IPC communication pin.
* There are several unknowns regarding this location. While only 1 bit is
significant and used to drive the COMDATA line, the masks used by the OS suggest
that 4 bits are used (bits 0..3).
Implemented bits:

0 = should be written as 0
1 = drives the COMDATA line. It also needs to be 1 to read IPC data
2 = should be written as 1
3 = should be written as 1

The COMDATA line itself is bidirectional and open collector so it can only be
pulled low by either the 8302 or the IPC end, so if any end is keeping it low,
further communication is impossible. Therefore, when the IPC needs to pass data
to the 8302, xxxx1110 should be written here.
It is currently unknown what the other 3 bits do, again their function is
inferred from IPC communications code. It is highly likely there is additional
hardware here but it is not used, perhaps due to bugs in the implementation? One
piece of data that strongly suggests this is that %00000001 is written here as
initialization during OS boot - while bit 0 is otherwise always reset on write.


$18020 READ IPC/MDV/SER status

Returns the status of various hardware controlling IPC communication,
microdrives and serial ports.

0 = NET data input
Returns the state of the NETIN pin

1 = transmit buffer full (when set)
* The documentation says 'microdrive transmit buffer is full' but since there is
only one transmit register ($18022), this is probably a common 'transmit buffer
full'. This should become 1 when the transmit register ($18022) is written, and
return to 0 when all bits in it are converted to serial format.

2 = microdrive read data ready (when set)
* Again, there is some ambiguity because there are two read data registers (one
for each of the two tracks on the tape) so more data is needed to explain when
exactly the bit goes high and what returns it to zero (Reading any of the two
microdrive read data registers, or a specific one?).

3 = microdrive data gap detected (when set)
This has to do with the formatting of the microdrive tape. A special signal is
coded onto the tape as a gap between sectors in order to provide 'slack' space
when the sector is over-written by new data. The microdrive has to be read to
find the correct position on the tape and the gap, in order to know when to
start writing. Again, some details would be nice.
* there is some ambiguity in the description of the function of this bit, which
may mean that either this bit is set when the microdrive is not running but goes
low whan the microdrive is running and pulses high when a gap is detected - or
that it is normally set and goes to zero when a gap is detected. More research
is needed...

4 = DTR1
Reflects the state of the DTR1L pin (pin 6), set when the serial port needs to
stop transmitting

5 = CTS2
Reflects the state of the CTS2L pin (pin 7), set when the serial port needs to
stop transmitting

6 = IPC acknowledge
This bit is zero when a data bit from the IPC is valid and can be read from bit
7 (see below) or the IPC is ready to receive a bit.
* Needs to be checked: this probably reflects the state of the COMCTL pin (pin
22)

7 = IPC data
This bit returns a data bit from the IPC. It should be considered valid only
when bit 6 (above) is set. This probably reflects the state of the COMDATA pin
(pin 35)

That's the 2 addresses in the ZX8302 that are concerned with IPC comms.


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

Re: Talking to the IPC: How do you do it from low level?

Post by tofro »

I think the SMSQ/E IPC code is a lot easier to understand than the Minerva stuff. I have stolen that to use in some of my own projects. Keyrow scanning, for example, is here: https://github.com/tofro/QL-Magnetic/bl ... /ipcom_asm Maybe it helps.


ʎɐqǝ ɯoɹɟ ǝq oʇ ƃuᴉoƃ ʇou sᴉ pɹɐoqʎǝʞ ʇxǝu ʎɯ 'ɹɐǝp ɥO
stephen_usher
Super Gold Card
Posts: 524
Joined: Tue Mar 11, 2014 8:00 pm
Location: Oxford, UK.
Contact:

Re: Talking to the IPC: How do you do it from low level?

Post by stephen_usher »

tofro wrote: Mon Apr 28, 2025 7:27 am I think the SMSQ/E IPC code is a lot easier to understand than the Minerva stuff. I have stolen that to use in some of my own projects. Keyrow scanning, for example, is here: https://github.com/tofro/QL-Magnetic/bl ... /ipcom_asm Maybe it helps.
Thanks, I'll take a look.

Without a deep understanding about how this works I can't write robust code to test things. Looking at code helps but it usually doesn't tell you *why* that's being done. I'm not sure how the protocol works either, for example how does the IPC know that there's a bit of data ready to be read? How do the ZX8302 and the IPC not clash on the single, bi-directional data line? That's the sort of thing I'm looking for.


stephen_usher
Super Gold Card
Posts: 524
Joined: Tue Mar 11, 2014 8:00 pm
Location: Oxford, UK.
Contact:

Re: Talking to the IPC: How do you do it from low level?

Post by stephen_usher »

Taken a look, but assembler isn't the best descriptive code, you often can't see the wood for the trees.

This assembler syntax has confused me:

Code: Select all

        lea     pc_ipcwr,a5
        lea     pc_ipcrd-pc_ipcwr(a5),a4 ; hardware addresses
Which addresses are these? Obviously two of the registers' addresses are being loaded into a4 and a5 but which ones?

I'm guessing that this is moving the value in bit 7 to bit 0

Code: Select all

        move.b  (a4),d0                  ; rx bit in msb
        add.b   d0,d0                    ; ... in X
        roxl.w  #1,d1                    ; ... in bit 0
And I'm not exactly sure what this code is doing...

Code: Select all

qlhc_wloop
        move.b  #pc.ipcwr>>2,d0          ; IPC write bits
        rol.w   #1,d0
        add.b   d0,d0                    ; bit to send in bit 1
        move.b  d0,(a5)
I'm guessing that "add.b d0,d0" is a quick way of doubling rather than than using a shift left? By why not just rotate 2 rather than one in the previous instruction?

The problem is that this is showing the mechanism and not the intention, i.e. describes what it's doing but not why it's doing it.


stephen_usher
Super Gold Card
Posts: 524
Joined: Tue Mar 11, 2014 8:00 pm
Location: Oxford, UK.
Contact:

Re: Talking to the IPC: How do you do it from low level?

Post by stephen_usher »

Trying to synthesise the data I have, I wonder if the lower four bits of the IPC write register ($18003) map directly to the pins on the ZX8302 chip, i.e.
  • Bit 0 - Pin 28 - RESETOUTL
  • Bit 1 - Pin 35 - COMDATA
  • Bit 2 - Pin 22 - COMCTL
  • Bit 3 - Pin 5 - BAUDX4
So, toggling Bit 3 clocks the data out, maybe?
Is COMCTL the data direction bit?
What triggers data output to the IPC?

Do I need to poll the IPC acknowledge bit in the status register to see if the IPC is trying to tell me something, read the message and then act upon it? I guess so as we have no interrupts.

So many questions.


Post Reply