0

I am writing a program in the 8085 assembly language to prove both the Demorgan's laws using any two arbitrary values, I am having to use many push-pop operations in the stack and also a lot of register operations. Writing the whole program as the main function is causing the code to get very cluttered and hard to follow along.

How can I use subroutines and stack operations in an efficient manner to make my programs more organised and simple in function? Any tips and guidance would be of great help.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • 1
    Your question is very broad. Can you show us some examples of the kinds of things you would like to change? `push` and `pop` are ubiquitous in 8085 assembly language programming as a way to save the contents of registers across function calls, so it's quite likely that the more subroutines you add the more `push/pop` operations you'll see. As your question is currently written, that's about the best "guidance" I can give you. More details with specific examples will encourage more and better answers. – Jim Mischel Jun 26 '23 at 16:53
  • 1
    General advice questions are usually not encouraged on Stack Overflow. If your code already works, but you want to see if it can be improved, you could post it to https://codereview.stackexchange.com. (They have very specific [rules](https://codereview.stackexchange.com/help/how-to-ask) for posts, so please read them carefully before posting there.) – Nate Eldredge Jun 26 '23 at 17:13
  • Generally speaking, 8085/Z80 is going to need to have a lot of push/pop for the simple fact that many operations can only be done with a particular register set. Sometimes you can use `XCHG` (`EX DE, HL` on Z80) to reduce the number of push/pops you need to do, but it's on a case-by-case basis and does make the code a little harder to read. – puppydrum64 Jun 30 '23 at 11:54
  • 1
    To add on to my previous comment I'd like to suggest using indents to make your code easier to read. I treat `push` and `pop` the same way curly braces are used in C and that's served me well over the years. Same with `xchg`, if I have any code between two `XCHG` statements I typically indent all of that so that it's clear that DE and HL are swapped during that "section" of code – puppydrum64 Jul 20 '23 at 15:55

1 Answers1

2

At a high level, you would turn your main into functions the same as if it were in a high level language, or pseudo code.  A function is a unit of abstraction, parameter passing is one key to the abstraction of functions.  Sometimes, however, we create a function that either is used only once or doesn't take any parameter (a weak abstraction), just to keep the main code a bit less cluttered.


As far as assembly language goes: You'll need a calling convention, as guidance at least.

In assembly we can customize the calling convention for each routine (though some consideration is required for function pointers, a feature you may not be using).

The calling convention will say what registers and/or stack to pass parameters, and how to receive the return value.  One convention is pass all parameters on the stack, but some others pass (some) arguments in registers, which is generally preferred, I think.  When passing parameters on the stack, you have the issue of who (caller or callee) deallocates them (pop them off the stack).

On some machines, parameters are passed in global variables (memory) instead of on the stack.  I think this is generally inefficient (and also makes the functions non-re-entrant, not capable of recursion).

Another approach is the very old "inline" parameter passing approach, where a parameter block of constants or pointers are placed immediately after the call instruction, and the callee picks them up from the code of the caller, and returns to after the parameter block.  This is great for the original FORTRAN where parameters are always passed by reference.

A calling convention will also say if there are any call-preserved registers.  On a machine with so limited registers, this is a two-edged sword, in that, if you use some registers as call-preserved, then you cannot use them for parameter passing, but callers will be able to keep some values in registers across a call.  A caller that wants to use a call-preserved register will assume that register is already in use by some function further up in the call chain, and so will push the register in function prologue and pop to restore that register in epilogue, so that the call-preserved register can be temporarily repurposed for this function.

Again, in assembly, we can customize the calling convention per routine, knowing the implementation of that routine and the implementation of the caller, you should be able to establish a fairly efficient approach to parameter passing.  Suggest for each routine then, documenting both parameters (i.e. in registers), return values, and also what registers are clobbered vs. which are preserved.  (At some point, you may find that a common convention is more useful than customizing for each routine.)

Erik Eidt
  • 23,049
  • 2
  • 29
  • 53
  • 1
    Upvoted, and a shameless plug for my guide on my (8086) assembly comments and style: https://pushbx.org/ecm/doc/acegals.htm#protocol describes the protocol comments that I use. They're invaluable to keep track of the calling conventions of many different functions. – ecm Jun 26 '23 at 17:54