Common mailbox/buffers for disconnecting the string handler from the driver code
Cluso99
Posts: 18,071
I am close to releasing the new formatter which is based on jonnymacs jm_fullduplexserial object. I've totally decoupled the formatter from the pasm driver(s) so that I now can run multiple copies of the formatter to support up to 16 port/slots (actually an arbitrary value).
I now have a multiport serial driver (up to 16 ports although I did a version of 64 with 58 tested) and an LCD Driver. The same formatter code runs them both, and will do so for other drivers such as VGA, Keyboard, etc.
However, I discovered that configuring the ports with pin numbers, baud, etc was a problem. So my solution is to have a configuration mailbox to pass the configuration parameters and the standard port mailbox which contains the buffer head/tail/start/end pointers to the respective buffers.
Passing all those parameters was complex to say the least. So I abandoned that method (was working).
The alternative is to just edit a big list in the program with pin numbers for the relevant ports supported. They can still be changed on the fly, but this makes it easier to describe and implement from a users point of view.
Now the problem. Many of you know I have a real problem with not being able to force the location of code/variables/buffers into specifi hub locations. Well, this just bit me on the a....
Here is my big >100 line set of parameters that supports
- Serial configuration (16 ports)
- LCD configuration (1 port)
- 16 port/slots of pointers to buffer head/tail/start/end
- A buffer for each of the used port/slots (ie can be <16)
I have it contained in a DAT section. PropTool/pnut doesn't support include or ifdef ![]()
Here is this part of the code
'' ====================================================================================================================
dat { port/slots configuration, parameters and buffers}
'' ====================================================================================================================
'' WARNING: Within the following groups, the order and size must be maintained !!!
port_lcd_config {3 longs} '\ LCD configuration
long @port_params '| ptr to port_params
byte 0<<7 | LCD_SLOT '| inactive | slot# where: 1=active, bbbb=slot#
byte LCD_CE '| LCD: Serial clock enable pin
byte LCD_RST '| LCD: Reset pin (can be reset by software)
byte LCD_DC '| LCD: Data / Command selection pin
byte LCD_SDA '| LCD: Serial Data MOSI pin
byte LCD_CLK '| LCD: Serial Clock pin
byte LCD_LED '| LCD: LED backlight enable pin (hi to enable - ~2.4mA)
byte LCD_SDO '/ LCD: Serial Data MISO pin
port_ser_config {3 longs} '\ Multiport SERIAL configuration
long @port_params '| ptr to port_params
port_control '| (keep together!)
byte {0} 0<<7 | 0<<6 | SER_RX {63} '| inactive | recv | pin# where: {slot} 1=active, 1=xmit, bbbbbb=pin#
byte {1} 0<<7 | 1<<6 | SER_TX {62} '| inactive | xmit | pin#
byte {2} 0 '| unused
byte {3} 0 '| unused
byte {4} 0 '| unused
byte {5} 0 '| unused
byte {6} 0 '| unused
byte {7} 0 '| unused
byte {8} 0 '| unused
byte {9} 0 '| unused
byte {10} 0 '| unused
byte {11} 0 '| unused
byte {12} 0 '| unused
byte {13} 0 '| unused
byte {14} 0 '| unused
byte {15} 0 '/ unused
port_params {16*4 = 64 longs} '\ max (16 ports) of 4 longs... port[slot]: buffer ptrs to p_head, p_tail, p_start, p_end
long {0} @BUF0 '| \ {slot 0} p_head SERIAL-RX (ptr to current head of buffer)
long @BUF0 '| | p_tail " (ptr to current tail of buffer)
long @BUF0 '| | p_start " (ptr to start of buffer)
long @BUF0+BUF_SIZE '| / p_end " (ptr to end+1 of buffer)
long {1} @BUF1 '| \ {slot 1} p_head SERIAL-TX
long @BUF1 '| | p_tail "
long @BUF1 '| | p_start "
long @BUF1+BUF_SIZE '| / p_end "
long {2} @BUF2 '| \ {slot 2} p_head LCD
long @BUF2 '| | p_tail "
long @BUF2 '| | p_start "
long @BUF2+BUF_SIZE '| / p_end "
long {3} 0 '| \ {slot 3} p_head -unused-
long 0 '| | p_tail "
long 0 '| | p_start "
long 0 '| / p_end "
long {4} 0 '| \ {slot 4} p_head -unused-
long 0 '| | p_tail "
long 0 '| | p_start "
long 0 '| / p_end "
long {5} 0 '| \ {slot 5} p_head -unused-
long 0 '| | p_tail "
long 0 '| | p_start "
long 0 '| / p_end "
long {6} 0 '| \ {slot 6} p_head -unused-
long 0 '| | p_tail "
long 0 '| | p_start "
long 0 '| / p_end "
long {7} 0 '| \ {slot 7} p_head -unused-
long 0 '| | p_tail "
long 0 '| | p_start "
long 0 '| / p_end "
long {8} 0 '| \ {slot 8} p_head -unused-
long 0 '| | p_tail "
long 0 '| | p_start "
long 0 '| / p_end "
long {9} 0 '| \ {slot 9} p_head -unused-
long 0 '| | p_tail "
long 0 '| | p_start "
long 0 '| / p_end "
long {10} 0 '| \ {slot 10} p_head -unused-
long 0 '| | p_tail "
long 0 '| | p_start "
long 0 '| / p_end "
long {11} 0 '| \ {slot 11} p_head -unused-
long 0 '| | p_tail "
long 0 '| | p_start "
long 0 '| / p_end "
long {12} 0 '| \ {slot 12} p_head -unused-
long 0 '| | p_tail "
long 0 '| | p_start "
long 0 '| / p_end "
long {13} 0 '| \ {slot 13} p_head -unused-
long 0 '| | p_tail "
long 0 '| | p_start "
long 0 '| / p_end "
long {14} 0 '| \ {slot 14} p_head -unused-
long 0 '| | p_tail "
long 0 '| | p_start "
long 0 '| / p_end "
long {15} 0 '| \ {slot 15} p_head -unused-
long 0 '| | p_tail "
long 0 '| | p_start "
long 0 '/ / p_end "
' Define buffers for up to 16 port/slots...
' -- 2 serial port/slots... (1 fdx uart serial ports)
' -- 1 LCD port/slot
BUF0 byte 0[BUF_SIZE] '\ serial buffer(s): tx must follow rx when using quick method "openfdxport"
BUF1 byte 0[BUF_SIZE] '/
BUF2 byte 0[BUF_SIZE] '> LCD buffer
'add more if required (max 16)
'' ====================================================================================================================
Now the problem is this gets relocated. But the real issue is that the longs that contain addresses eg
- @port_params
- @BUF0
- etc
all insert an incorrect relative address into these hub locations. So when I pull these by reading via pasm or via long[port_control] I get the relative address, not the actual address where the buffers, etc are physically located.
As you can see, there are quite a number for a little piece of code that's about to grow substantially as more ports/drivers are added.
If the code is compiled without spin, then the physical hub addresses can be forced, and they work as expected.
Any ideas how to get past this without the cumbersome mess of physically re-plugging every corrected pointer back into the table?

Comments
Here is a piece of spin code writing out to the same formatter object, but being redirected to either the serial port or the LCD. It works but the initialisation is terrible.
Declare the objects
Open the objects (note the parameters are now simplieied)
And to use the serial and lcd objects
ser.fstr0(string("------------------------------------------------\r\n")) ser.fstr0(string("LCD driver v232 and handler v060 \r\n")) ser.fstr1(string(" Serial driver started in cog %1x\r\n"), sercog-1) ser.fstr1(string(" LCD driver started in cog %1x\r\n"), lcdcog-1) lcd.fstr0(string("------------------------------------------------\r\n")) lcd.fstr0(string("LCD driver v232 and handler v060 \r\n")) lcd.fstr1(string(" Serial driver started in cog %1x\r\n"), sercog-1) lcd.fstr1(string(" LCD driver started in cog %1x\r\n"), lcdcog-1)One potential suggestion was to compile the block as pasm only where the addresses would be correct, and then load the binary into the man code.
While this would work for me, the real benefit of this method is for general use where we can have a single "format" object and many "driver" objects all working harmoniously together.
These are the supported methods in the formatter object
con { pst formatting } var pub null() con { start routines } PUB start_fdx_handler(p_params, rxslot, p_rxbufstart, bufsize) : status | p_rxparam_slot PUB start_rx_handler(p_params, xslot, p_bufstart, bufsize) : status | p_param_slot PUB start_tx_handler(p_params, xslot, p_bufstart, bufsize) : status | p_param_slot con { transmit routines } pub txChr(chr) : status | head, new pub txStr(p_str) pub txStrCR(p_str) pub txCR() pub txASCII(chr) pub txDump(addr1, addr2) | addr, addrend, i, j, mask pub txn(chr, n) pub txFlush() pub setStripLF(n) ' 0=OFF, 1=ON con { receive routines } pub rxChr() : chr | tail, new pub rxStr(p_str) | addr, chr pub rxCheck() : chr PUB rxPeek() : status pub rxAvail() : count pub rxTime(ms) : chr | mstix, t pub rxTix(tix) : chr | t pub rxFlush() pub setEcho(n) ' 0=OFF, 1=ON con { transmit unformatted strings } pub str(p_str) pub substr(p_str, len) | chr pub padstr(p_str, width, pad) con { transmit formatted strings } pub fstr0(p_str) pub fstr1(p_str, arg1) pub fstr2(p_str, arg1, arg2) pub fstr3(p_str, arg1, arg2, arg3) pub fstr4(p_str, arg1, arg2, arg3, arg4) pub fstr5(p_str, arg1, arg2, arg3, arg4, arg5) pub fstr6(p_str, arg1, arg2, arg3, arg4, arg5, arg6) pub format(p_str, p_args) | idx, c, asc, field, digits pri lcase(c) : result pri get_nargs(p_str) : p_str1, val1, val2 | c, sign con { transmit decimal/hex/octal/quarternary/binary values } pub fmt_number(value, base, digits, width, pad) pub dec(value) pub fdec(value, digits) pub jdec(value, digits, width, pad) pub dpdec(value, dp) pub jdpdec(value, dp, width, pad) pub hex(value) pub fhex(value, digits) pub jhex(value, digits, width, pad) pub oct(value) pub foct(value, digits) pub joct(value, digits, width, pad) pub qrt(value) pub fqrt(value, digits) pub jqrt(value, digits, width, pad) pub bin(value) pub fbin(value, digits) pub jbin(value, digits, width, pad) pub nstr_fmt_number(value, radix, digits, width, pad) : p_str ' *** changed 19 AUG 2020 *** pub nstr_dec(value, digits) : p_str | sign, len pub nstr_usdec(value, digits) : p_str | len pub nstr_dpdec(value, dp) : p_str | len, byte scratch[12] pub nstr_itoa(value, radix, digits) : p_str | sign, len, d pub nstr_revstr(p_str) : result | first, len, last pub nstr_padstr(p_str, width, padchar) : p_pad | len con { documentation } pub start(p_portctl, rxxport, rxpin, rxmode, rxbaud, p_rxstart, p_rxend, txxport, txpin, txmode, txbaud, p_txstart, p_txend) pub openport(p_portctl, xport, xpin, xdirn, xmode, xbaud, p_xstart, p_xend) : status | spmode, baudcfg pub closeport(p_portctl, xport, xpin) pub setStripLF(n) ' 0=OFF, 1=ON optionally strip <lf> when transmitting characters pub setEcho(n) ' 0=OFF, 1=ON optionally echo received characters to the transmitter pub txChr(chr) | n, p, head, new '* xmit a character pub txStr(p_str) '* xmit a formatted string pub txStrCR(p_str) '* xmit a formatted string followed by <cr><lf> pub txCR() '* xmit <cr><lf> pub txASCII(chr) '* xmit a character (converts $00-$1F & $7F-$FF to ".") pub txDump(addr1, addr2) | addr, addrend, i, j, mask '* dumps hub memory as lines of 16 bytes + ASCII pub txn(chr, n) '* xmit a character 'n' times pub txFlush() | p ' wait until the xmit buffer and uart are empty pub rxChr() : chr | p, head, tail, new '* recv a character pub rxStr(p_str) | addr, chr '* recv a 0 terminated string (<cr> terminates entry) pub rxCheck() : chr | p ' checks rx buffer and returns -1 (True) if no chars available, else returns a char PUB rxPeek() : stat | p ' checks rx buffer and returns -1 (True) if no chars available, else 0 (False) pub rxAvail() : count | p ' checks rx buffer and returns the number of characters in the buffer, 0 = none pub rxTime(ms) : chr | mstix, t '* waist up to ms for a character, returns -1 (True) if no chars available, else returns a char pub rxTix(tix) : chr | t '* waits up to clock-ticks for a char, returns -1 (True) if no chars available, else returns a char pub rxFlush() ' flushes (clears) the rx buffer pub str(p_str) '* xmit an un-formatted string pub substr(p_str, len) | chr '* xmit an un-formatted string with a maximum length pub padstr(p_str, width, pad) '* xmit an un-formatted string of 'width' characters padded using the 'pad' character pub fstr0(p_str) '* xmit a formatted string pub fstr1(p_str, arg1) '* xmit a formatted string with 1 variable pub fstr2(p_str, arg1, arg2) '* xmit a formatted string with 2 variables pub fstr3(p_str, arg1, arg2, arg3) '* xmit a formatted string with 3 variables pub fstr4(p_str, arg1, arg2, arg3, arg4) '* xmit a formatted string with 4 variables pub fstr5(p_str, arg1, arg2, arg3, arg4, arg5) '* xmit a formatted string with 5 variables pub fstr6(p_str, arg1, arg2, arg3, arg4, arg5, arg6) '* xmit a formatted string with 6 variables pub format(p_str, p_args) | idx, c, asc, field, digits '* xmit a formatted string with n variables pub fmt_number(value, base, digits, width, pad) ' -- decimal/hex/octal/quarternary/binary pub dec(value) ' -- decimal pub fdec(value, digits) ' -- decimal and... pub jdec(value, digits, width, pad) ' -- decimal and... pub dpdec(value, dp) ' -- decimal and... pub jdpdec(value, dp, width, pad) ' -- decimal and... pub hex(value) ' -- hex pub fhex(value, digits) ' -- hex and... pub jhex(value, digits, width, pad) ' -- hex and... pub oct(value) ' -- octal pub foct(value, digits) ' -- octal and... pub joct(value, digits, width, pad) ' -- octal and... pub qrt(value) ' -- quarternary pub fqrt(value, digits) ' -- quarternary and... pub jqrt(value, digits, width, pad) ' -- quarternary and... pub bin(value) ' -- binary pub fbin(value, digits) ' -- binary and... pub jbin(value, digits, width, pad) ' -- binary and...You will probably recognise most of them! They are from jonnymacs fantastic jm-fullduplexserial and jm_nstr objects. Thanks Jon