The Novels

Economics 101, a Novel (Rough Draft) -- My first sustained attempt at a novel, two-thirds finished in rough draft, and heading a little too far south.
What would you do if you and your study partner, with whom you had been seriously discussing marriage, suddenly found yourselves all alone together on a desert island? Study economics?
Sociology 500, a Romance (Second Draft) -- The first book in the Economics 101 Trilogy.(On hold.)
Karel and Dan, former American football teammates and now graduate students, meet fellow graduate students Kristie and Bobbie, and the four form a steady study group.

Featured Post

Sociology 500, a Romance, ch 1 pt 1 -- Introducing Bobbie

TOC Well, let's meet Roberta Whitmer. Bobbie entered the anthropology department office and looked around. Near the receptionis...

Monday, January 13, 2020

Backup: 33209: School -- Parameter Stack

[JMR202104051713: Backup of https://joelrees-novels.blogspot.com/2020/01/33209-school-parameter-stack.html.]

Chapter 3.5: School -- BASIC vs. Pascal vs. Assembler

Chapter 3.6: School -- Reference Locality in Software

"I think we should talk a little more about variables and variable names. Remember this?" Professor Crane put the second Pascal version of the shoesz program up on the overhead projector.

program shoesz;

{ Print shoe size table with halves, by Thomas Bright and Rusty Crane }

procedure wrhalf( size: Real );
var
  ipart, numer: Integer;
begin
  ipart := trunc(size);
  numer := trunc((size-ipart+0.25)*2);
  if numer > 1 then
    begin
      numer := 0;
      ipart := ipart+1;
    end;
  write (ipart);
  if numer > 0 then 
    write (' 1/2')
end;

var conv: Char;
  st_sz, end_sz, sz, conv_sz: Real;
  dbl_sz: Integer;

const cm_in = 2.54;

begin
  conv := ' ';
  while (conv<>'E') AND (conv<>'M') do
  begin
    writeln ('Shoe Sizes');
    writeln ('Type E for English to Metric.');
    writeln ('Type M for Metric to English: ');
    readln (conv)
  end;

  writeln ('Start: ');
  readln (st_sz);
  writeln ('End: ');
  readln (end_sz);

  if conv = 'E' then
    writeln ('English => Metric')
  else
    writeln ('Metric => English');

  { Step by halves. }
  for dbl_sz := round(st_sz)*2 to round(end_sz)*2 do 
  begin
    sz := dbl_sz/2.0;
    if conv = 'E' then
      conv_sz := sz*cm_in
    else
      conv_sz := sz/cm_in;
    wrhalf(sz);
    write (chr(9), '=> ');
    wrhalf(conv_sz);
    writeln (chr(9), '(', sz , '=> ', conv_sz, ')')
  end
end.

"That was just what, two days ago?"

"Two classes ago. I think I have my copy here."

Several us dug out our notes from that day.

Professor Crane continued, "What do you think would happen if the size variable, sz, in the main procedure were to be renamed 'size', with the exact same spelling as the parameter size in the write-half routine?"

"Problems?" asked Lisa.

Mike's forehead furrowed in thought as he studied his copy of the source. "Well, you're calling the write-half routine from different places, with different variables. That means the names of the parameters you give it aren't going to conflict with the parameter name in the function."

I studied my own copy. "I think Mike's right. A size variable declared inside of write-half would probably conflict with the size parameter, but Pascal keeps the variable itself on the stack, and it must keep the name of variables local to the half write procedure separate in the dictionary somehow."

Professor Crane rolled his eyes and shook his head, then nodded. "They call that dictionary the symbol table."

Mike turned to give me a sharp look. "Stack?"

George groaned. "What's a stack?"

Pat reached over and poked George in the ribs. "Weren't either of you listening to me the other day?"

Everyone else turned toward Professor Crane. Becky asked, "What are they talking about?"

Professor Crane chuckled. "Something we call locality of reference. In Pascal and certain other languages, variables, constants, and other identifiers are only visible within the block they are declared in, and within blocks declared within the same block."

Pat and I exchanged glances and nods of understanding. Mike, George, and Lisa nodded, hesitantly. The rest of the class just gave him blank looks or shook their heads.

Professor Crane traded the slide for another:

program factfib;

function factorial( number: Integer ): Integer;
  begin
    if number > 0 then
      factorial := number*factorial(number-1)
    else
      factorial := 1;
  end;

function fibonacci( number: Integer ): Integer;
  begin
    if number > 1 then
      fibonacci := fibonacci(number-1)+fibonacci(number-2)
    else 
      fibonacci := number; 
  end;

var number: Integer;

begin
  for number := 0 to 7 do
  writeln( number, ': factorial:', factorial(number),
' fibonacci:', fibonacci(number) ); end.

"What do you think will happen? Will it be able to keep track of all those numbers?"

There were nods and shakes of the head and blank looks.

Andrea said, "I sure can't keep track of them all." 

She and Becky exchanged looks full of doubt.

"What do you think, Pat?" Professor Crane grinned.

Pat nodded. "Sure."

"How about you, Joe?"

"Oh, yeah. No problem. I'm not sure how the symbols are kept local to each scope in the symbol table, but the values are safe on the stack." I started doodling in 68000 assembler again.

"George?"

George scratched his chin. "Wouldn't factorial calling itself overwrite number?"

"Mike?"

"Joe and Pat keep saying something about stacks. Is that something magic where you can stack up the numbers and the compiler somehow keeps track of them all?"

"Very good." Professor Crane swapped slides again. 

"Does that look like a stack of plates to you? How's my artwork?"

There were a few complaints, but mostly hesitant nods. I put my pencil down to focus.

"If you put a plate on the stack, where do you put it? Usually, I mean."

Dirk volunteered, "In the center, especially if it's my mom asking me to."

"I don't know you." Lisa turned her head away and sniffed exaggeratedly.

Dirk snickered.

Professor Crane suppressed a chuckle. "And where do you usually take one off from?" 

Andrea said, "From the top, of course. Why?"

"We can make stacks in programs, using an array and a pointer." He swapped slides again.

"The stack pointer points to the number most recently pushed on the stack. If we pop one off, we read it from where the stack pointer is pointing and decrement the stack pointer. If we push another on, we increment the stack pointer and then store it where the stack pointer points." 

Pat was following along okay, I thought I was, Mike was hanging in there, George was not quite, and the rest of the class were showing various states of confusion. 

Professor Crane showed us a slide with the following demonstration code in BASIC, and walked us through the initialization, the push and pop subroutines, and the execution of the main part:

10 REM PUSH AND POP SUBROUTINES 20 DIM S(10) 30 LET T = -1 REM STACK POINTER 40 LET V0 = -9999 REM TOP OF STACK TEMPORARY 100 V0 = 10 110 GOSUB 1010 120 V0 = 20 130 GOSUB 1010 140 V0 = 30 150 GOSUB 1010 160 V0 = -9999 170 GOSUB 1110 180 PRINT "POPPED "; V0 190 GOSUB 1110 200 PRINT "POPPED "; V0 210 GOSUB 1110 220 PRINT "POPPED "; V0 1000 END 1010 REM PUSH SUBROUTINE 1020 IF T = 10 THEN GOTO 12OO 1030 T = T + 1 1040 S(T) = V0 1050 RETURN 1100 REM 1110 REM POP SUBROUTINE 1120 IF T = -1 THEN GOTO 1300 1130 V0 = S(T) 1140 T = T - 1 1150 RETURN 1200 REM 1210 REM PUSH ERROR 1220 PRINT "STACK FULL ERROR. CAN'T PUSH "; V0; "." 1230 STOP 1300 REM 1310 REM POP ERROR 1320 PRINT "STACK EMPTY ERROR. CAN'T POP." 1330 STOP

Then he showed us a printout of what running it produced:

POPPED  30
POPPED  20
POPPED  10 

We discussed the code for a few minutes, then he showed us demonstration code in Pascal, again walking us through the initialization, pop function, push procedure, and the main execution:

program stackdemo;

const limit = 10;

var

  stack: array[0 .. limit] of Integer;
  tos: Integer;
  errorcode: Integer;

procedure push( value: Integer );
  begin
    if tos < limit then
      begin
        tos := tos + 1;
        stack[ tos ] := value;
      end
    else
      begin
        errorcode := 1;
        writeln( 'Stack full ', errorcode, '. Can''t push ', value, '.' )
      end
  end;

function pop: Integer;
  begin
    if tos >= 0 then
      begin
        pop := stack[ tos ];
        tos := tos - 1;
      end
    else
      begin
        errorcode := 2;
        writeln( 'Stack empty ', errorcode, '. Can''t pop.' );
        pop := -9999
      endfc
  end;
 
begin
  TOS := -1;
  errorcode := 0;
  push( 10 );
  push( 20 );
  push( 30 );
  writeln( 'Popping ', pop );
  writeln( 'Popping ', pop );
  writeln( 'Popping ', pop );
  if errorcode <> 0 then
    writeln( 'errorcode: ', errorcode );
end.

The output was essentially the same as the output of the BASIC demo code:

Popping 30
Popping 20
Popping 10

Becky raised her hand. "Can I see that slide that shows the stack of plates and the, uhm, number stack again?" 

Professor Crane grinned and swapped slides.

Becky thought for a moment, then asked, "Why? uhm, ... in the subroutines, you're only adding one or subtracting one, but the picture of the array shows, what is that? They're all even numbers. That's adding two. Why?"

"Very good." Professor Crane adjusted his slide. "Very observant, Becky. Pat, what do you have to say about this? Joe? Mike?"

The three of us exchanged looks, then Pat nodded to Mike. "What do you think, Mike? You got this?"

He shook his head.

She turned to me, and I lifted my fist, showing gu. "Rock, scissors, paper."

She gave me a quizzical look.

"No jung? Toss a coin, then?" I reached for my pocket.

"You start."

I refrained from pulling out my coin purse. "I'm not sure I can make it make sense."

"Me neither."

I nodded my head in resignation. "Well, okay." I turned to Becky and said, "Address versus index. The picture of the stack array is showing addresses in memory, but the code uses numeric indexes into the array."

Pat nodded. Out of the corner of my eye I saw the light come on in Mike's eyes. 

Becky's eyes reflected something that looked like the beginning of understanding.

Pat asked, "Integers in Pascal are like, two bytes in memory, Professor Crane?" 

"Yep. Well, with the compiler we have, yes."

Becky's eyes lit up, then the flame flickered and was replaced again by doubt. She hesitated, then asked, "So  one in Pascal is like two in assembler or memory or whatever that is?"

"Right!" Mike exclaimed. "Pascal converts the index number to an address. Multiplies the index by the size of the elements and adds the base address of the array." He paused, then continued. "But ... the BASIC array is going to be more than two, right? BASIC variable are floating point, not integers."

Now Becky looked more confused.

Professor Crane nodded in agreement. "Yep. Most BASICS don't have small integer variables. Just floating point, and BASIC floating point numbers tend to be five bytes, as a tradeoff between memory requirements and range."

Becky hesitated again before asking, "So the index would be multiplied by five instead of two?"

Professor Crane smiled and nodded in confirmation.

Becky responded with a proud smile of her own.

After a few more minutes of discussion, Professor Crane looked at me and raised his eyebrows.

"What?"

He grinned. "I thought I saw you doodling a few minutes back.

I glanced at the code I had jotted down before the lesson had gotten interesting. 

"Not really."

He looked disappointed. "So you haven't converted the Fibonacci program to 68000 assembler while we've been discussing how local variables work? Or worked out an assembler version of the stack demonstration code?"

"I got interested in class."

That got a laugh from most of the students.

"Oh."

I scratched behind my ear. "Besides, the stack demonstration wouldn't really be all that interesting."

"Why not?"

"68000 machine language has push and pop built in."

"Built-in, huh?"

"Well, I guess they don't test for overflow or underflow."

"No?"

"I guess the CPU architects leave stack bounds checking to the memory management hardware."

He raised his eyebrows and nodded. "Yes, most CPUs are like that, although I hear Intel's iAPX 432 might be different. So you don't want to show us what the demonstration program would look like in 68000 assembler?"

I set my chin to the right and my mouth the left,  and pinched my nose before scratching my chin. "With no bounds checking?"

"Sure."

"Okay." I stood and went to the whiteboard, and wrote out the following, pausing at moments for thought, but not commenting vocally:

STKSZ EQU 11

SSTACK DS.W STKSZ
PSTACK DS.L 64
RSTACK DS.L 64

MESSAGE DC.B 'Popping '
DC.B 0 ; end of string START MOVE.L #RSTACK+STKSZ*4,A7 ; for return addresses MOVE.L #PSTACK+STKSZ*4,A6 ; for the parameters MOVE.L #SSTACK+STKSZ*2,A5 ; for the demonstration stack * Main code
* Push 16 bit values on 32 bit stack: MOVE.W #10,-(A5) CLR.W -(A6) ; High word first in memory. MOVE.W #20,-(A5) CLR.W -(A6) ; High word MOVE.W #30,-(A5) CLR.W -(A6) ; High word * Pop and print MOVE.L #MESSAGE,-(A6) BSR PSTRING MOVE.L (A5)+,-(A6) BSR PINT BSR PCRLF MOVE.L #MESSAGE,-(A6) BSR PSTRING MOVE.L (A5)+,-(A6) BSR PINT BSR PCRLF MOVE.L #MESSAGE,-(A6) BSR PSTRING MOVE.L (A5)+,-(A6) BSR PINT BSR PCRLF RTS

Professor Crane frowned. "Your stacks are all upside down."

"Most CPU stacks are, aren't they?"

He turned to the class. "Can any of you guys tell what his code does?"

Even Pat was a little bemused. "Can you make it look more like the Pascal and BASIC demonstration code?" she asked.

I grumbled jokingly as I turned back to the board, erased it, and started rewriting, vocalizing both the code and the comments:

* Fully emulating the demonstration stack:
SSTKSZ EQU 11	; Software stack
SSTACK DS.W SSTKSZ	; of 16-bit integers
SSTKPTR DS.W 1	; Don't even need 16 bits.
* Planning on doing it upside-up, with base in A5.

"I could keep the index in D7, too. But I guess we want to see me manipulate the stack pointer as an index, anyway."

ERRORCODE DS.L 1

STKSZ EQU 64
PSTACK DS.L STKSZ ; parameter stack
RSTACK DS.L STKSZ ; return stack
* Both are traditional pushdown.

"Sixty-four levels ought to be enough for whatever the library and OS need."

"Two stacks?" Professor Crane asked.

"I think I like two stacks."

MESSAGE_STR
 DC.B 'Popping '
 DC.B 0

"Not counted strings?" Professor Crane asked.

"Seems easier this way. No compiler to keep track of the count for me, and this way I don't need to keep track by hand."

"Makes sense."

PUSHERRMSG_01
 DC.B 'Stack full. ERROR '
 DC.B 0
PUSHERRMSG_02
 DC.B ': Can''t push '
 DC.B 0
PUSHERRMSG_03
 DC.B '.'
 DC.B 0

I lifted my marker. "Yeah, that's a lot of bits of the message spread out. Oh, well."

"No formatted I/O in the library code?"

"Well, then I would have to explain the library code."

"Ah. Less to explain this way." Professor Crane seemed satisfied.

I continued:

PUSHERR
 MOVE.L #PUSHERRMSG_01,-(A6)
 BSR PSTRING
 MOVE.L ERRORCODE,-(A6)
 BSR PINT
 MOVE.L #PUSHERRMSG_02,-(A6)
 BSR PSTRING
 BSR PINT ; value waiting on parameter stack
 BSR PCRLF
 MOVE.L #PUSHERRMSG_03,-(A6)
 BSR PSTRING
 JMP ERREXIT

"Tedious, but nothing difficult to understand. Put what we want to print on the stack, call the routine to print it."

* Upside-up stack!
* A5 is demonstration stack base.

"Oh, I should probably have put the base in a variable, too. But I'm doing it this way, now." 

"Let's see how it turns out."

PUSH
 MOVE.W SSTKPTR,D7
 CMP.W #STKSZ,D7
 BHS PUSHERR ; unsigned

"That loads the stack pointer, and then compares it to the size of the stack. By using an unsigned compare, we can skip testing the lower bound of zero. But the Pascal source didn't check the opposite end, anyway."

"True."

 ADD.W #1,D7
 MOVE.W D7,SSTKPTR ; update pointer
 ASL.W #1,D7 ; adjust index for 16-bit array

"Arithmetic shifting left is the same as multiplying by two." 

Professor Crane added, "Becky, there's your conversion between index and offset to an address." 

"Oh-kay." But she didn't really sound all that okay, yet.

 MOVE.L (A6)+,D0 ; pop 32 bits of parameter

"A6 in paranthesis says A6 points to the source value. The plus after says that the CPU updates the pointer after the move, by adding the size of what it moved to D0. The parameter stack that A6 points to is 32 bits wide, to save code. That's four bytes, so A6 gets four added to it."

 MOVE.W D0,(A5,D7.W) ; store 16-bit integer

"The value stored was only our 16-bit integer, so we only need to store two bytes. And that funky looking index expression in the parenthesis here says that A6 has the base address of the array, and D7 has the offset -- the index we multiplied by two up there. The CPU adds the base and the offset to get the address of the element of the array to store it in."

Becky hesitated before saying, "A6 plus D7 is the address where it's stored?"

"Yeah." 

"What's that period double-u mean?"

"W stands for word, and a 16-bit integer in 68000 assembler is called a word. An 8-bit integer is B for byte, and a 32-bit integer is L for long."

"Hmm. Maybe I'm seeing this."

"Great."

 RTS

"The return from subroutine instruction pops the return address from the return stack and loads it into the program counter, which is what Motorola calls the instruction pointer. And the pop routine just reverses this process."

POPERRMSG_01
 DC.B 'Stack empty. ERROR '
 DC.B 0

"Can't tell anyone about a value that doesn't exist."

POPERRMSG_02
 DC.B ': Can''t pop.'
 DC.B 0
*
POPERR
 MOVE.L #POPERRMSG_01,-(A6)
 BSR PSTRING
 MOVE.L ERRORCODE,-(A6)
 BSR PINT
 MOVE.L #POPERRMSG_02,-(A6)
 BSR PSTRING
 BSR PCRLF
 JMP ERREXIT
*
POP
 MOVE.W SSTKPTR,D7 ; Not testing for too high.

"On the 68000, loading a data register sets the condition codes, so we already know if it's zero or minus. Minus would be a stack underflow. And will ignore testing against the other end of the stack here."

 BMI POPERR
 ASL.W #1,D7 ; adjust index for 16-bit array
 MOVE.W (A5,D7.W),D0
EXT.L D0 ; Make it 32-bit

"We'll extend the sign bit, since we'll be passing it to library functions."

 MOVE.L D0,-(A6)
 SUB.W #1,SSTKPTR ; update pointer

"Conveniently, the 68000 allows us to add that one to the index in memory, so we don't have to keep an extra copy in a register." 

"Like you don't have enough extra registers to keep a copy," Professor Crane joked.

"Heh."

 RTS

START
* Runtime.

"First, we set up the return stack pointer so we can do subroutine calls, and the parameter stack pointer so we can pass parameters."

 MOVE.L #RSTACK+STKSZ*4,A7 ; for return addresses
 MOVE.L #PSTACK+STKSZ*4,A6 ; for the parameters 

"Wait a minute." Professor Crane stopped me.

"Yeah?"

"Oh, yeah, you said you like two stacks."

"I think so."

"Does the 68000 have instructions to support stack frames?"

"There's a link instruction and an unlink instruction."

"Hmm. Go ahead."
 MOVE.L #SSTACK,A5 ; emulating the demonstration stack
 MOVE.L #0,SSTKPTR ; demonstration stack index

Becky muttered, "Okay, there's the base of that array, and the index." 

I nodded and continued writing:

* Main code:
* push values
 MOVE.W #10,-(A6)
 CLR.W -(A6) ; High word first in memory.

"Woops. That should also be sign-extended." I thought for a minute, erased the last line, and changed the line before it:

 MOVE.L #10,-(A6)

Pat asked, "The 68000 is most significant bit first?" 

"Yeah."

"Isn't that inefficient?"

"Ask the engineers. It sure seems easier for human eyes to read." 

I have much stronger arguments about byte order now, but we don't want to interrupt the story too much.

 BSR PUSH
 MOVE.L #20,-(A6)
 BSR PUSH
 MOVE.L #30,-(A6)
 BSR PUSH
* Pop and print
 MOVE.L #MESSAGE,-(A6)
 BSR PSTRING
 BSR POP
 BSR PINT
 BSR PCRLF
 MOVE.L #MESSAGE,-(A6)
 BSR PSTRING
 BSR POP
 BSR PINT
 BSR PCRLF
 MOVE.L #MESSAGE,-(A6)
 BSR PSTRING
 BSR POP
 BSR PINT
 BSR PCRLF
 RTS 

When I turned around,  only Daren, Andrea, and Alex still looked lost.

"So, how would that change if you used stack frames?" Professor Crane's eyebrows were raised.

"On a single stack?"

"Most system engineers don't want to go to the trouble of supporting two stacks."

"I sure don't know why. But with something this simple, there really isn't a reason for frames at all."

"That might make it easier to understand the frames."

 "Maybe. But I don't think I remember exactly what the link and unlink instructions do. How about if I just show where we can save a stack frame pointer in this code?"

"You can?"

"Pretty simple, really. On routine entry, you just push a copy of the parameter stack pointer, probably on the return stack to keep the parameter stack clean and give the frame pointer some protection. Then just remember to pop it on exit."

I wrote a fragment on the board:

SUBROUTINEA
 MOVE.L A6,-(A7) ; Mark frame.
 ...
 MOVE.L (A7)+,A6 ; Force restore frame.
 RTS 

Or, since the parameter stack is balanced, instead of loading the parameter stack pointer back, just  drop the saved frame pointer off the return stack:"

SUBROUTINEA
 MOVE.L A6,-(A7) ; Mark frame.
 ...
 ADD.L #4,A7 ; Drop frame.
 RTS 
"Either way, you can load the frame pointer from the return stack whenever you need it."

"How do you tell the parameters of a recursive call from those of a non-recursive call?"

I had to think for a moment. "Maybe you have to mark the frame before you call the subroutine. Then, I guess, when a routine calls itself recursively, it would do something else:"

ROUTINEA
... MOVE.L A6,-(A7) ; Mark frame.
BSR RECROUTINE ADD.L #4,A7 ; Drop frame.
... RTS
*
RECROUTINE
...
MOVE.L 4(A7),A4 ; Saved PC is top. MOVE.L A4,-(A7) ; Mark old frame.
BSR RECROUTINE ADD.L #4,A7 ; Drop frame.
... RTS

 Professor Crane looked at my code with a puzzled expression. "That looks like it might work, actually."

The bell rang.

"Okay, we'll continue this next class. Homework is to write the Fibonacci series code recursively in BASIC."

Groans echoed through the room.

"And, Joe, can you look up the link and unlink instructions and bring some code using them?"

"Sure."

Chapter 3.7: 
TOC

[Current backup at https://joel-rees-economics.blogspot.com/2020/01/bk-33209-school-parameter-stack.html.
Developed February to April 2021, notes at https://joel-rees-economics.blogspot.com/2020/01/notes-33209-school-basic-vs-pascal-vs.html.
Extracted and expanded from https://joel-rees-economics.blogspot.com/2020/01/bk01-33209-school.html.
Earlier backup at https://joel-rees-economics.blogspot.com/2020/01/bk-33209-school.html.]

No comments:

Post a Comment

Keep it on topic, and be patient with the moderator. I have other things to do, too, you know.