Let's start with the basics -- the concept of parameter versus control blocks.
I'm going to assume you're familiar with DOS or Unix, where you have a function available "open" to a file.
Code:
/* In C syntax, you might see something like this: */
int fileHandle;
fileHandle = open("/tmp/whatever", O_READONLY);
if(fileHandle == -1)
perror("Cannot open file because: ");
We might express this in 6502 assembly language like so:
Code:
LDA #<openParams
LDX #>openParams
JSR _open
STA fileHandle
BPL 1$
LDA #<errorMsg
LDX #>errorMsg
JSR _perror
1$:
; ...etc...
fileHandle:
.db 0
openParams:
.dw filename, O_READONLY
errorMsg:
.db "Couldn't open file because: ",0
filename:
.db "/tmp/whatever",0
In this case, the data structure defined by "openParams" is a "parameter block." It's used to pass inputs (and sometimes outputs for sophisticated-enough routines) to the library function you're calling. Note that parameter blocks look just like stack frames in C, except that they don't reside in the stack. They can sit anywhere in memory, and can even be re-used for other purposes if you can prove that nothing else will call into the OS or library while using the structure address.
Once inside the open() call, you need to save the pointer to the parameter block:
Code:
_open:
STA param
STX param+1
;
; work, work, work,
;
LDY #0
LDA (param),Y
STA filenameLow
INY
LDA (param),Y
STA filenameHigh
;
; etc...
;
The difference between a parameter block and a control block is one of how much state resides in the block of memory. For example, suppose our OS keeps track of device ID using a 16-bit number, starting sector number (16-bit), current sector number (16-bit), byte offset (16-bit) within that sector. (For example, this would be plenty sufficient to work with a FAT filesystem.) Then we'd need to expand our code accordingly:
Code:
openParams:
.dw filename, O_READONLY ; these are inputs to the open() call
.dw 0, 0, 0, 0 ; these are maintenance state variables used by other filesystem calls
This no longer serves just the purpose of parameters, but now also has control information (used by the OS). Thus, it's now a
control block. The up-side of this is that you save memory (no need for handle tables, etc.), but the down-side is that you directly expose the implementation details of your library or OS. Thus, whenever you want to change your OS, you almost certainly will break application compatibility (what happens if you don't use FAT? NTFS has no concept of a "starting block" for a file, for example, but rather "extents belonging to forks").
One should, ideally, minimize the use of control blocks, unless you can guarantee for all time that you have all the state you'll ever need. Experience maintaining software suggests this never is the case, though, which is why MS-DOS evolved from file control blocks (FCBs) from it's CP/M pedigree to using more Unix-like file handles starting with 2.0. It also allowed MS-DOS to support subdirectories, which FCB's cannot handle, since FCBs (as CP/M implemented them) embedded the entire filename (11 characters) within, instead of pointing to the filename.
Note that passing a bunch of parameters on the stack implies you're building a parameter block, and you're passing the address to this parameter block using the stack pointer register.