MICROSEQ(9)
π microseq β ppbus microsequencer developer's guide π
okay bestie, welcome to MICROSEQ(9), the ppbus microsequencer developer's guide from the Kernel Developer's Manual. this one's a chonker so buckle up π
β¨ SYNOPSIS β¨
#include <sys/types.h>
#include <dev/ppbus/ppbconf.h>
#include <dev/ppbus/ppb_msq.h>
πΈ DESCRIPTION πΈ
so first things first, if you want the ppbus description and the general tea about the microsequencer, go read ppbus(4). that's your homework. π
the whole point of this document? it's giving recruiting. like, the goal is to encourage developers to actually use the microsequencer mechanism so we get:
- a uniform programming model
- efficient code
and before you even think about touching microsequences, bestie, please go look at the ppc(4) microsequencer implementation, plus an example of how to use it over in vpo(4). do your research, it's free π
π PPBUS register model π
π Background π
the parallel port model we picked for ppbus? it's the PC parallel port model. so literally every register described down below has the exact same semantic as its counterpart in a PC parallel port. no cap. if you want more info about ISA/ECP programming, go grab the Microsoft standard referenced as "Extended Capabilities Port Protocol and ISA interface Standard". and just so we're clear β the registers described later on are standard parallel port registers.
mask macros are defined in the standard ppbus include files for each valid bit of the parallel port registers. they're right there for you, use them π«Ά
π Data register π
in compatible or nibble mode, writing to this register will drive data to the parallel port data lines. in any other mode, drivers may be tri-stated by setting the direction bit (PCD) in the control register. and reading from this register gives you back the value on the data lines. simple as β¨
π¦ Device status register π¦
this one's read-only, and it reflects the inputs on the parallel port interface. here's the lineup:
- bit 7 β
nBUSYβ inverted version of parallel port Busy signal - bit 6 β
nACKβ version of parallel port nAck signal - bit 5 β
PERRORβ version of parallel port PERROR signal - bit 4 β
SELECTβ version of parallel port Select signal - bit 3 β
nFAULTβ version of parallel port nFault signal
the others are reserved and return an undefined result when read. so like, don't build your whole personality on reading those bits, okay? π
π§ Device control register π§
this register directly controls several output signals and enables some functions. main character energy honestly:
- bit 5 β
PCDβ direction bit in extended modes - bit 4 β
IRQENABLEβ1enables an interrupt on the rising edge ofnAck - bit 3 β
SELECTINβ inverted and driven as parallel port nSelectin signal - bit 2 β
nINITβ driven as parallel port nInit signal - bit 1 β
AUTOFEEDβ inverted and driven as parallel port nAutoFd signal - bit 0 β
STROBEβ inverted and driven as parallel port nStrobe signal
okay that's the register model section done and dusted, but we are SO not finished yet β more parts incoming, don't go anywhere bestie πΈ
π MICROINSTRUCTIONS π
β¨ Description β¨
okay so every microinstruction is one of these four things: a parallel port access, a program iteration, a submicrosequence, or a C call. and the parallel port? bestie, you must think of it as the logical model described in ppbus(4). that's the whole vibe, no cap.
here's the entire lineup of microinstructions you get to play with:
#define MS_OP_GET 0 /* get <ptr>, <len> */
#define MS_OP_PUT 1 /* put <ptr>, <len> */
#define MS_OP_RFETCH 2 /* rfetch <reg>, <mask>, <ptr> */
#define MS_OP_RSET 3 /* rset <reg>, <mask>, <mask> */
#define MS_OP_RASSERT 4 /* rassert <reg>, <mask> */
#define MS_OP_DELAY 5 /* delay <val> */
#define MS_OP_SET 6 /* set <val> */
#define MS_OP_DBRA 7 /* dbra <offset> */
#define MS_OP_BRSET 8 /* brset <mask>, <offset> */
#define MS_OP_BRCLEAR 9 /* brclear <mask>, <offset> */
#define MS_OP_RET 10 /* ret <retcode> */
#define MS_OP_C_CALL 11 /* c_call <function>, <parameter> */
#define MS_OP_PTR 12 /* ptr <pointer> */
#define MS_OP_ADELAY 13 /* adelay <val> */
#define MS_OP_BRSTAT 14 /* brstat <mask>, <mask>, <offset> */
#define MS_OP_SUBRET 15 /* subret <code> */
#define MS_OP_CALL 16 /* call <microsequence> */
#define MS_OP_RASSERT_P 17 /* rassert_p <iter>, <reg> */
#define MS_OP_RFETCH_P 18 /* rfetch_p <iter>, <reg>, <mask> */
#define MS_OP_TRIG 19 /* trigger <reg>, <len>, <array> */
π§ Execution context π§
the execution context of microinstructions is, like, the little state bundle the sequencer carries around:
- the program counter, which points at the next microinstruction to execute β either in the main microsequence or inside a subcall
- the current value of
ptr, which points at the next char to send/receive - the current value of the internal branch register
and here's the tea: this data gets modified by some of the microinstructions. not all of them. just some. π
π MS_OP_GET and MS_OP_PUT
these two are the microinstructions you use to do either predefined standard IEEE1284-1994 transfers or programmed non-standard io. pick your fighter. π
π MS_OP_RFETCH β Register FETCH π
grabs the current value of a parallel port register, slaps a mask on it, and saves the result into a buffer. iconic.
Parameters:
- register
- character mask
- pointer to the buffer
Predefined macro: MS_RFETCH(reg,mask,ptr)
π MS_OP_RSET β Register SET π
asserts and/or clears some bits of a particular parallel port register. two masks get applied. double the masking, double the slay.
Parameters:
- register
- mask of bits to assert
- mask of bits to clear
Predefined macro: MS_RSET(reg,assert,clear)
π MS_OP_RASSERT β Register ASSERT π
asserts all the bits of a particular parallel port register. no holding back, full send.
Parameters:
- register
- byte to assert
Predefined macro: MS_RASSERT(reg,byte)
β³ MS_OP_DELAY β microsecond DELAY β³
delays the execution of the microsequence. yes it's in microseconds, yes it's tiny, yes it matters.
Parameter:
- delay in microseconds
Predefined macro: MS_DELAY(delay)
π MS_OP_SET β SET internal branch register π
sets the value of the internal branch register. that's it, that's the whole job.
Parameter:
- integer value
Predefined macro: MS_SET(accum)
π MS_OP_DBRA β Do BRAnch π
branches if the internal branch register, decremented by one, comes out positive. countdown behaviour fr fr.
Parameter:
- integer offset in the current executed (sub)microsequence. Offset is added to the index of the next microinstruction to execute.
Predefined macro: MS_DBRA(offset)
π¦ MS_OP_BRSET β BRanch on SET π¦
branches if some of the status register bits of the parallel port are set.
Parameter:
- bits of the status register
- integer offset in the current executed (sub)microsequence. Offset is added to the index of the next microinstruction to execute.
Predefined macro: MS_BRSET(mask,offset)
π¦ MS_OP_BRCLEAR β BRanch on CLEAR π¦
branches if some of the status register bits of the parallel port are cleared.
Parameter:
- bits of the status register
- integer offset in the current executed (sub)microsequence. Offset is added to the index of the next microinstruction to execute.
Predefined macro: MS_BRCLEAR(mask,offset)
π MS_OP_RET β RETurn π
returns from a microsequence. and listen β this instruction is mandatory. it's literally the only way the microsequencer can detect the end of the microsequence. skip it and you're lost in the sauce forever, no exaggeration. π
the return code comes back in the integer pointed at by the (int *) parameter of the ppb_MS_microseq().
Parameter:
- integer return code
Predefined macro: MS_RET(code)
π MS_OP_C_CALL β C function CALL π
calls C functions from microsequence execution. super handy when a non-standard i/o is being performed to grab a data character off the parallel port.
Parameter:
- the C function to call
- the parameter to pass to the function call
the C function shall be declared as a int(*)(void *p, char *ptr). the ptr parameter is the current position in the buffer currently scanned.
Predefined macro: MS_C_CALL(func,param)
π MS_OP_PTR β initialize internal PTR π
initializes the internal pointer to the currently scanned buffer. this pointer gets passed to any C call (see above, bestie, keep up π ).
Parameter:
- pointer to the buffer that shall be accessed by
xxx_P()microsequence calls. Note that this pointer is automatically incremented duringxxx_P()calls
Predefined macro: MS_PTR(ptr)
π΄ MS_OP_ADELAY β do an Asynchronous DELAY π΄
makes a tsleep() happen during microsequence execution. and the tsleep is executed at PPBPRI level, just so you know exactly where you're napping.
Parameter:
- delay in ms
Predefined macro: MS_ADELAY(delay)
π MS_OP_BRSTAT β BRanch on STATe π
branches on a status register state condition.
Parameter:
- mask of asserted bits. Bits that shall be asserted in the status register are set in the mask
- mask of cleared bits. Bits that shall be cleared in the status register are set in the mask
- integer offset in the current executed (sub)microsequence. Offset is added to the index of the next microinstruction to execute.
Predefined macro: MS_BRSTAT(asserted_bits,clear_bits,offset)
β©οΈ MS_OP_SUBRET β SUBmicrosequence RETurn β©οΈ
returns from the submicrosequence call. this action is mandatory before a RET call β do not skip it, it's giving "I read the docs and I respect them". also heads up: some microinstructions (PUT, GET) may not be callable within a submicrosequence.
No parameter.
Predefined macro: MS_SUBRET()
π² MS_OP_CALL β submicrosequence CALL π²
calls a submicrosequence. and a submicrosequence is just a microsequence with a SUBRET call in it. that's the definition, periodt.
Parameter:
- the submicrosequence to execute
Predefined macro: MS_CALL(microseq)
π«Ά MS_OP_RASSERT_P β Register ASSERT from internal PTR π«Ά
asserts a register with data currently pointed at by the internal PTR pointer.
Parameter:
- amount of data to write to the register
- register
Predefined macro: MS_RASSERT_P(iter,reg)
π«Ά MS_OP_RFETCH_P β Register FETCH to internal PTR π«Ά
fetches data from a register, and the data lands in the buffer currently pointed at by the internal PTR pointer.
Parameter:
- amount of data to read from the register
- register
- mask applied to fetched data
Predefined macro: MS_RFETCH_P(iter,reg,mask)
β‘ MS_OP_TRIG β TRIG register β‘
triggers the parallel port. this microinstruction exists to give you very efficient control of the parallel port, and honestly? it's the main character. triggering a register is: writing data, waiting a while, writing data, waiting a whileβ¦ which is exactly how you get magic sequences out to the port. β¨
Parameter:
- amount of data to read from the register
- register
- size of the array
- array of unsigned chars. Each couple of u_chars define the data to write to the register and the delay in us to wait. The delay is limited to 255 us to simplify and reduce the size of the array.
Predefined macro: MS_TRIG(reg,len,array)
πΈ MICROSEQUENCES πΈ
π§± C structures π§±
here's the shape of the things, exactly as they are:
union ppb_insarg {
int i;
char c;
void *p;
int (* f)(void *, char *);
};
struct ppb_microseq {
int opcode; /* microins. opcode */
union ppb_insarg arg[PPB_MS_MAXARGS]; /* arguments */
};
π οΈ Using microsequences π οΈ
to instantiate a microsequence, just declare an array of ppb_microseq structures and initialize it however you need. you can either use the predefined macros, or code your microinstructions directly according to the ppb_microseq definition. either way works, you're valid. π
for example:
struct ppb_microseq select_microseq[] = {
/* parameter list
*/
#define SELECT_TARGET MS_PARAM(0, 1, MS_TYP_INT)
#define SELECT_INITIATOR MS_PARAM(3, 1, MS_TYP_INT)
/* send the select command to the drive */
MS_DASS(MS_UNKNOWN),
MS_CASS(H_nAUTO | H_nSELIN | H_INIT | H_STROBE),
MS_CASS( H_AUTO | H_nSELIN | H_INIT | H_STROBE),
MS_DASS(MS_UNKNOWN),
MS_CASS( H_AUTO | H_nSELIN | H_nINIT | H_STROBE),
/* now, wait until the drive is ready */
MS_SET(VP0_SELTMO),
/* loop: */ MS_BRSET(H_ACK, 2 /* ready */),
MS_DBRA(-2 /* loop */),
/* error: */ MS_RET(1),
/* ready: */ MS_RET(0)
};
see how some of those parameters are left undefined? they must be filled in before you execute the microsequence. to initialize each microsequence, you'd use the ppb_MS_init_msq() function like this:
ppb_MS_init_msq(select_microseq, 2,
SELECT_TARGET, 1 << target,
SELECT_INITIATOR, 1 << initiator);
and then you execute the microsequence. in that order. obviously. π
π The microsequencer π
the microsequencer runs either at ppbus or adapter level (go peek at ppbus(4) if you want the info about the ppbus system layers). most of the microsequencer is executed at ppc level, specifically to avoid the ppbus-to-adapter function call overhead. but some actions β like deciding whether the transfer is IEEE1284-1994 compliant β are executed at ppbus layer instead. β¨
π BUGS π
okay bestie, listen, even iconic code has its little quirks, and this page is legally required to tell you about them, so here we go π
- submicrosequences? one level only, babe. you get one level of submicrosequences and that is IT. no nesting a submicrosequence inside a submicrosequence inside a submicrosequence. the recursion stops here. it's giving "we ran out of stack and also patience." π
- triggering the port has a delay ceiling. when you trigger the port, the maximum delay allowed is 255 us. that's the cap, that's the limit, do not pass go, do not collect a longer delay. 255 us and not one microsecond more, periodt. π
fr fr, remember both of these before you go writing your microsequences, because these aren't suggestions, they're the actual constraints of the thing. lowkey important. β¨
and that's the whole page, bestie!! π«Ά you now know microseq better than most people who claim they know microseq. go forth and sequence your micros responsibly. πΈ
this little love letter to the FreeBSD kernel came from the Debian crew, dated June 6, 1998 β yes it's older than most of us and it still ate πβ¨ go drink some water and reread the BUGS section one more time, iykyk π
π¦ where this page comes from
- debian bookworm: freebsd-manpages 12.2-1 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- debian trixie: freebsd-manpages 12.2-2 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- ubuntu jammy: freebsd-manpages 12.2-1 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- ubuntu noble: freebsd-manpages 12.2-1 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- ubuntu questing: freebsd-manpages 12.2-2
- ubuntu resolute: freebsd-manpages 12.2-2build1 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- ubuntu stonking: freebsd-manpages 12.2-2build1 Β· BSD-2-Clause-FreeBSD AND BSD-4-Clause
- Copyright (c) 1998, 1999, Nicolas Souchu
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms