fprwards and backwards logic in IF statements (in assembly)

The place for codemasters or beginners to talk about programming any language for the Spectrum.
Wall_Axe
Manic Miner
Posts: 501
Joined: Mon Nov 13, 2017 11:13 pm

fprwards and backwards logic in IF statements (in assembly)

Post by Wall_Axe »

in normal languages you can do an IF statement with multiple conditions quite easily, but assembly is a bit like having the memory of a goldfish..as registers get trashed all the time.

So far, I have been doing things like:testing for the opposite of what im looking for and then skipping over code if the conditions are met.
then testing for the next condition will be handled the same way , so multiple conditions for one IF statement are possible.
Its kind of a pain but works

Another way of doing it with forwards logic would be to test for what im looking for but then call a function, if i wanted multiple conditions to be met it would be another function inside that. The only prob is that leads to so many functions or subroutines being made, as opposed to new labels being made in the above example. The functions would have silly names as they need to be different each time like:
AlmostMoveCharacterLeft:
(test for more things that would stop a character moving left)
AlmostAlmostMoveCharacterLeft:
(test for one more thing then actually move him)

Has anyone found a better way?
equinox
Dynamite Dan
Posts: 1079
Joined: Mon Oct 08, 2018 1:57 am
Location: SE England

Re: fprwards and backwards logic in IF statements (in assembly)

Post by equinox »

Can't you reserve a byte of memory for what you're doing, and set bits as state flags? Or is that dumb/inefficient for some reason?
User avatar
SkoolKid
Manic Miner
Posts: 416
Joined: Wed Nov 15, 2017 3:07 pm

Re: fprwards and backwards logic in IF statements (in assembly)

Post by SkoolKid »

Wall_Axe wrote: Mon Oct 16, 2023 7:07 pm Has anyone found a better way?
If you need to perform multiple tests before doing something, I'd place all the tests, along with the code that does the something, in a single subroutine. The subroutine starts by doing test 1, and returns if it fails. Same for tests 2, 3, etc. If all the tests pass (i.e. you haven't returned yet), you reach the code that does the something.

That's my thoughts on this, anyway. And I acknowledge that I might have completely misunderstood your question.
SkoolKit - disassemble a game today
Pyskool - a remake of Skool Daze and Back to Skool
Wall_Axe
Manic Miner
Posts: 501
Joined: Mon Nov 13, 2017 11:13 pm

Re: fprwards and backwards logic in IF statements (in assembly)

Post by Wall_Axe »

I am using bytes for state flags etc.

and yes the second answer is correct for those types of situations. Like the movecharacterleft


the situation where i use backwards logic is: see if the option chosen is the first - then execute it, if not then see if it is the second then execute that

I jp across the code which executes option 1 , then if its not option 2 jp over that code
Last edited by Wall_Axe on Mon Oct 16, 2023 7:25 pm, edited 1 time in total.
User avatar
Joefish
Rick Dangerous
Posts: 2080
Joined: Tue Nov 14, 2017 10:26 am

Re: fprwards and backwards logic in IF statements (in assembly)

Post by Joefish »

Not that I know of!

If it's a case of "IF A THEN DO X" and there's no "ELSE" function then you can either code it as "IF NOT A THEN SKIP THIS NEXT BIT... (DO X)... CARRY ON" or as "IF A THEN CALL X", so whether you call a function and return, or take no action, you end up in the same place. The former is probably quicker, and doesn't use the stack, but you won't be able to use function X from anywhere else in your code if it's written in-line like that. The latter is more faff but maybe more readable later, and would allow you to call function X from other parts of your code too.

The danger with using CALL is if you have several checks each calling different functions, a return from the first one will land you right back in the middle of your IF checks, rather than at the end of them all. In that case, you might want to do something like put the address you want to return to in HL before doing your IF checks, then jump to the function, and the function either pushes HL onto the stack and does a return, or ends with a JP (HL).

Really whatever you pick is going to be a trade-off between being quick to write (like the first example), more structured and easier to follow (use CALL to separate named functions), speed of processing (where you might write the most common path in-line for speed, and all other cases branch off), or memory efficient (where you might cut out more functions into separate calls so you can re-use them, instead of duplicating simpler tasks whenever you need them, for speed).

And if you have a whole tree of decisions and sub-branches, it's another trade-off between having everything in one place, and branching off to do those sub-clauses. It can be hard to read if everything is broken down and spread across several separate functions, but then you're more likely to fluff up the logic and branching if you try and do it all in one go instead of breaking it down. There's no real answer to that in assembly; that's why structured languages like C exist, to make programming these things easier!
User avatar
ParadigmShifter
Manic Miner
Posts: 951
Joined: Sat Sep 09, 2023 4:55 am

Re: fprwards and backwards logic in IF statements (in assembly)

Post by ParadigmShifter »

An example of such a condition might help.

You're writing an adventure style game (although menu driven) so you should probably look at how stuff like the Quill handles conditions there, basically

High level (global) conditions - these are checked every time the game state advances
Local (room based) conditions - each room has a list of things to check which can't be done in other locations (e.g. opening a door with a key)

You can also have item based (or skill based) conditions which are checked depending on what you are carrying or if you have a particular skill.

GAC had low priority conditions as well I think which were checked last of all and after player input was processed I seem to remember.

Then you just shoot through all the conditions which are active (either via functions or a jump table to handler routines which jump back to the function which pulls items out of the condition lists to check until there are no more.

I have something like that but for drawing stuff (which I add to a queue)

Code: Select all

	ld hl, (drawListPtr) ; note all my draw routines are aligned to 256 byte boundary so this is a single byte, low byte is always 0
	; otherwise I'd have to check high byte and low byte being 0 which would take longer. also shortens the amount of data for each list item

nextSprite:
	ld a, (hl) ; list is terminated by a single 0
	or a
	jr z, doneDraw
	inc hl
	ld (drawListPtr), hl
	ld l, 0 ; this is the address of the drawing function to call
	ld h, a
	jp (hl) ; call it. It will use (drawListPtr) set earlier to remove arguments it wants and set hl to correct value once it has processed the arguments

doneDraw:
	ld hl, drawList
	ld (drawListPtr), hl
sample drawing code

Code: Select all

	ALIGN 256
movebufftoscreen:
	ld hl, (drawListPtr)

	ld e, (hl)
	inc hl
	ld d, (hl)
	inc hl
	ld a, (hl)
	inc hl
	ld b, (hl)
	inc hl
	ld (drawListPtr), hl

	; have our parameters in DE and B and A now

	; do things with the stack :) code removed to avoid complicating stuff
	; it basically just does H = A, L = B
	; then copies 2 bytes at a time (HL++) to (DE++) 8 times, so like this in C
	
	; for(b = 8; b > 0; --b) { 
	;    *de++=*hl++;
	;    *de++=*hl++;
	; }
	
	; it's a bit more complicated than that cos of the speccy screen layout of course

	ld hl, (drawListPtr) ; set HL to correct value for next item in the list (will point to 0 if there are no more)

	jp nextSprite ; jump back to handler
EDIT: And I've not even optimised list handling or the draw functions yet, I know they can be made more efficient (but they are fast enough atm so that can come later if there are speed issues).

EDIT2: If you do it like that though you probably want a function that says "don't bother checking any more things, state has changed" e.g. you moved into another room, opened another exit, etc. would likely do that.
Ralf
Rick Dangerous
Posts: 2304
Joined: Mon Nov 13, 2017 11:59 am
Location: Poland

Re: fprwards and backwards logic in IF statements (in assembly)

Post by Ralf »

Well, I would say that's not that hard :)

Let's suppose you want to do:

IF conditionA AND conditionB AND conditionC THEN MakeActionX
ELSE MakeActionY

In a mix of assembly and pseudocode it could be:

TestConditionA
JP NZ, MakeActionY

TestConditionB
JP NZ, MakeActionY

TestConditionC
JP NZ, MakeActionY

JP MakeActionX


As you can see, you need to do it step by step, not in a single instruction.
We assume here that by testing some condition, you set the zero flag. If condtion is true then the flag is set, otherwise it is reset.

And remember to name your variables and labels wisely. AlmostAlmostMoveCharacterLeft is almost certainly a bad name ;)
User avatar
ParadigmShifter
Manic Miner
Posts: 951
Joined: Sat Sep 09, 2023 4:55 am

Re: fprwards and backwards logic in IF statements (in assembly)

Post by ParadigmShifter »

For my movement code I check all the conditions and only if all collision checks pass etc. do I update the piece position

So for moving left it is:

is column <= 1? ; at left hand edge
jr z, donemoveleft
; are we at an even column? we are always ok to move then, since collision is 2 cells wide
is column even?
jr z, allowmoveleft
; checks collision now on the left
collided with something?
if yes: jr donemoveleft
allowmoveleft:
; update piece position
donemoveleft:

etc.

EDIT: Anything more complicated I'd go down the list(s) of conditions to check though (some lists can be static, e.g. a list per room). You may want a flag on each condition to say "this no longer occurs, do not check" (e.g. once you have used a key on a door don't check it again)

EDIT2: Might be worth stubbing out your condition handlers on a PC in C or some other high level language (I'd use C# personally) that you can easily convert to Z80 once you have it working as intended on the PC.

EDIT3: When I say "list" I mean just an array (of variable length items) terminated by NULL (or you can store a length if you want, probably harder to maintain that though). If a handler bails out early (e.g. it sees its condition is disabled) remember to set the next item to process pointer correctly (by skipping any parameters you did not remove from the list during processing).

So if you have a handler with 5 bytes of parameters but by the time you have processed the first one, you see it does not apply, remember to set the current list pointer to the next item, not where you got to when extracting the parameters.
User avatar
Seven.FFF
Manic Miner
Posts: 753
Joined: Sat Nov 25, 2017 10:50 pm
Location: USA

Re: fprwards and backwards logic in IF statements (in assembly)

Post by Seven.FFF »

Ralf has put his finger on the way that we do it. You can easily have a mix of OR and AND conditions, too. The condition code just threads through a series of checks, one at a time. You don't need to track the state of where you are at within the condition checking in an additional register, the current position in the code tracks that automatically:

Code: Select all

IF ((conditionA OR conditionB) AND conditionC) THEN MakeActionX
ELSE MakeActionY
In the same mix of assembly and pseudocode it could be this, which also has shortcircuiting (you can tell that because there are two separate jumps to MakeActionY, and if the first jump is taken then conditionC is never checked. And if the IsAOrB jump is taken then conditionB is never checked):

Code: Select all

TestConditionA
JP Z, IsAOrB

TestConditionB
JP NZ, MakeActionY

IsAOrB:

TestConditionC
JP NZ, MakeActionY

JP MakeActionX
Last edited by Seven.FFF on Mon Oct 16, 2023 9:07 pm, edited 9 times in total.
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
NXtel NXTP ESP Update ESP Reset CSpect Plugins
User avatar
ParadigmShifter
Manic Miner
Posts: 951
Joined: Sat Sep 09, 2023 4:55 am

Re: fprwards and backwards logic in IF statements (in assembly)

Post by ParadigmShifter »

For super complicated decisions you can use an expression tree and you can do those pretty easily if you use reverse polish notation (so like Forth) for that. You can use a stack then to traverse the tree

so

IF (A and B) OR (C and D)

turns into

A B AND C D AND OR

where A, B, C D are atoms (T or F, for true or false) so they just push themselves onto a stack (does not have to be the machine stack pointed to by SP, probably easier if it isn't)

AND and OR consume the top 2 items on the stack and replace it with T or F as appropriate. When the expression is finished you are left with either T or F on the top of the stack.

You can also do short-circuit evaluation that way.

LISP syntax is ok for that as well (OR (AND A B) (AND C D))

So to evaluate if(T OR F)

T F OR

1: push T onto stack. stack: T

2: push F onto stack. stack: F T

3: evaluate OR -- pops F and T, F OR T is true, push T onto stack. stack: T

we are done, top of stack is T, which is result of T OR F

EDIT: Obvs more useful with a more concrete example, say you have a function HERE which takes an item and pushes T or F onto the stack depending upon whether it is in the room or carried and UNLOCKED which takes a lockable object and pushes whether it is locked or not. Then you can have

KEY HERE CHEST HERE CHEST UNLOCKED NOT AND AND

would mean if the chest is NOT UNLOCKED AND the chest is HERE AND so is the KEY, this condition would be true. NOT pops the top of the stack and pushes F if it was T and vice versa

Advantage of doing something like that is the expression evaluator is very simple even for complicated expressions. If you know all functions (like HERE, AND, OR, NOT, UNLOCKED) have addresses > #4000 you can just use DW to define a list of words to check (objects would have IDs < #4000). You can also make items 8 bits if you want but that makes it more complicated to evaluate (since you need to know what is a function and what is an object ID)

Evaluator:

look at first item in list (so KEY). It's < #4000, so push it to the evaluation stack
second item is HERE which is a function since it is >= #4000. Call it. It removes KEY from the stack and pushes T or F depending on whether it is here or held

etc. until you reach the end of the data for that condition (length or terminate it somehow).

If the stack has more than 1 item on it at the end of all that, it's an error (unless you want to do subroutines which are also very easy to do with this method). Popping an empty stack is always an error though ;)
Wall_Axe
Manic Miner
Posts: 501
Joined: Mon Nov 13, 2017 11:13 pm

Re: fprwards and backwards logic in IF statements (in assembly)

Post by Wall_Axe »

thats an interesting way of using the stack, might have to use that later.
User avatar
ParadigmShifter
Manic Miner
Posts: 951
Joined: Sat Sep 09, 2023 4:55 am

Re: fprwards and backwards logic in IF statements (in assembly)

Post by ParadigmShifter »

Most compilers do something like that as an intermediate step for expression evaluation.

As I said, it's very simple.

Don't use the actual machine (SP) stack though :) EXX is an excellent choice for things like that though (always have your expression stack in H'L' and pop into D'E' say, with B'C' a count of how many things are on the stack say?) Just reserve a workspace area of say 256 bytes or less for the expression stack (and report an error and stop if you push too much too it in a debug build - which will then tell you if you did something wrong or you need to make your workspace area bigger in future).

Also, don't mess with the actual SP (by changing it to something else, with the idea of changing it back later) unless you really know what you are doing.

You might want to push a tag for each item onto the stack (so TAG_NUMBER, TAG_ITEM, TAG_FUNCTION) etc. to make deciding what is actually on the stack when you pop it easier (another reason not to use the processor stack - you might want to push 8 bit items onto it). Giving TRUE and FALSE their own tags might be handy as well (they can just be tags then, no additional data).

For messages you can then have

TAG_MSG, msgID, TAG_FUNCTION, displayMessage, END_OF_EXPRESSION

and calling subroutines can have

TAG_FUNCTION, mySubroutine, TAG_CALL, (... stuff to do after mySubroutine returns here)

which stores the point in the list you were at - you can usually use the stack for that as well (or you might have a separate stack just for subroutine return addresses - that's probably easier thinking about it), sets the list pointer to mySubroutine list instead, executes that until TAG_RETURN or END_OF_EXPRESSION, etc.

If the RPN thing gets on your nerves you can write a parser on the PC which turns normal looking expressions into RPN data.

EDIT: Anyway, golden rule is, when expressions start getting complicated it's usually easier to make your code data driven than end up with loads of complicated logic in ASM (and loads of code). With a data-driven expression evaluator you only have a small amount of code that is debugged and tested :)

I'm sure ketmar will have more forth like tips as well :)
Wall_Axe
Manic Miner
Posts: 501
Joined: Mon Nov 13, 2017 11:13 pm

Re: fprwards and backwards logic in IF statements (in assembly)

Post by Wall_Axe »

i suppose thts almost like creating a new language, but might be necessary for some complicated things.

The most complicated things ive ran into so far were:

Code: Select all

if keypressed
is_key_pressed=1

else

if is_key_pressed=1

do_action()
is_key_pressed=0
endif

endif
which in assembly is:

Code: Select all

call read_keyboard

LD A,-1
CP B

jp Z,keyispressed

LD A,(uppressed)
CP 1

JP NZ,dontmovearrowup

call optionsup

LD A,0
LD (uppressed),A

JP dontmovearrowup

keyispressed:
LD A,1
LD (uppressed),A
ret

dontmovearrowup:
i did that using jp,nz only. so it was convoluted not very readable but works well.


ive gotten into the habit of doing the backwards logic so i just use it,even though it might be easier to do it using ret and call
the keyboard read function you shared uses it,hence the labels upisnotpressed:
User avatar
ParadigmShifter
Manic Miner
Posts: 951
Joined: Sat Sep 09, 2023 4:55 am

Re: fprwards and backwards logic in IF statements (in assembly)

Post by ParadigmShifter »

I'd separate your move and select logic from the menu contents.

Have a current menu ptr which knows how many items are in the menu and call menuupdown as soon as you read the keyboard into B, which knows that the value will be in the B register.

Something like this for menu data

Code: Select all

mainmenu db 3 ; num items
    dw option0text ; pointer to text for option 0
    dw onoption0pressed; handler for option 0
    dw option1text ; pointer to text for option 1
    dw onoption1pressed; handler for option 1
    dw option2text ; pointer to text for option 2
    dw onoption2pressed; handler for option 2

currentmenu dw mainmenu ; start at mainmenu
currentmenuoption db 0 ; first option in the list
and have the menu handler code just inc and dec (currentmenuoption), including wraparound if you want that

if you need parameters for your handler functions you can add them as well. Best to use a fixed amount of parameters (1 byte or 2 bytes) for all functions, don't make the number of parameters variable length, if you need more than 2 bytes put a pointer to the parameter in the 2 bytes instead. This assumes the function you call knows what to do with the parameters it is passed (usually passed in DE)

To work out the pointer to optionNtext for option N you just need to calculate

(currentmenu) + 1 + currentmenuoption * 4 ; since we have 2 words of data per menu option at the moment. the add 1 byte is because we have the number of items as the first byte so need to skip over that

You can do a multiply by 4 by doing

ADD HL, HL
ADD HL, HL

then add on the currentmenu pointer to that. The handler to call is 2 bytes after that

When you press the select button set HL to the onoptionNpressed function and make a call to there. The easiest way to call a function through a pointer is to have the following function in your code (which does not return)... you only need 1 copy of this function (you can even find this instruction in the ROM and use that if you want lol)


JPHL:
jp (hl)

then when you want to call it use

call JPHL

then returning from the handler will all magically work as if you called the function

Note jp (hl) is terribly named it should be jp hl

so doing this will call my_func then return and get stuck in an infinite loop (so you can then bring up the debugger and verify you are in the endless loop).

ld hl, my_func
call JPHL
; test... go into an infinite loop so we can check in the debugger this is where we returned to
.endlessloop
jr .endlessloop


my_func:
ret

try that and you will see it calls my_func then when it returns it is at the endless loop in the debugger.


EDIT: If you do something like that you don't have to do stuff like

ld a, -1
cp b ; is b -1?

you can just do something like this

ld hl, currentmenuoption
ld a, (hl)
add b ; subtract 1 if b was -1, otherwise add 1
; verify we haven't gone off top or bottom of menu (wrap it around if you like)

; if all is ok
ld (hl), a
User avatar
ParadigmShifter
Manic Miner
Posts: 951
Joined: Sat Sep 09, 2023 4:55 am

Re: fprwards and backwards logic in IF statements (in assembly)

Post by ParadigmShifter »

Although I think you said you are responding to key releases rather than keypresses in which case stash the previous value of B in a variable, and if after you read the keyboard you find up/down is not pressed, LD B with the last keypress instead.

A better way would be to have a key repeat timer though which you could add to the read_keyboard routine (or do it just after that returns).

Then you can make the key respond as soon as you press it (increment a timer for every frame you are pressing it), and when that timer gets to the repeat time, set the timer back to 0 (or 1) and set the "up/down" pressed state again, so it does not go off every frame.

In pseudo code

Code: Select all

initalise updownrepeat timer to 0 at start of game

call read keyboard
if up or down was pressed (you'll notice my routine returns 0 if both are pressed same time so you don't need to worry about that)
; so you can just ld a, b : or a : jr nz, upordownwaspressed or something
    increment updownrepeattimer
else updownrepeattimer = 0

if (updownrepeattimer > REPEAT_DELAY)
     updownrepeattimer =1

if(updownrepeattimer == 1)
    perform the action
If you do it that way, it always responds as soon as you press up or down for the first time but then doesn't respond again until you hold it down for REPEAT_DELAY frames. But if you release and repress it, it responds immediately again.

You can get fancier and have REPEAT_REPEAT timer as well where you do things like

as soon as key pressed -> respond
if held down -> send the key again after REPEAT_DELAY frames
if you keep holding it -> send the key every REPEAT_REPEAT frames now instead

which is how the spectrum basic editor/input system works (and you can set both the delay times by poking the sysvars)

EDIT: If you use timers like that though you will want to put a HALT instruction at the end of your main loop so you only update every 1/50th of a second.

Also only call read_keyboard once a frame and stash the results if you need them later on. Ideally you would process all the input as soon as read_keyboard returned though
Wall_Axe
Manic Miner
Posts: 501
Joined: Mon Nov 13, 2017 11:13 pm

Re: fprwards and backwards logic in IF statements (in assembly)

Post by Wall_Axe »

Putting the function pointer with the menu is a good idea, I'll have to study it a bit more though
Wall_Axe
Manic Miner
Posts: 501
Joined: Mon Nov 13, 2017 11:13 pm

Re: fprwards and backwards logic in IF statements (in assembly)

Post by Wall_Axe »

I suppose the situation I'm thinking about is wanting to do something AND return. Whereas you can only do one or the other using basic assembly commands.

Storing the location on a stack could be risky for creating bugs?
User avatar
ParadigmShifter
Manic Miner
Posts: 951
Joined: Sat Sep 09, 2023 4:55 am

Re: fprwards and backwards logic in IF statements (in assembly)

Post by ParadigmShifter »

I don't get what you are on about there, sorry.

What do you mean by "wanting to do something and return"? Do you mean return from a function after it does something or return from a menu?

You can have as many ret statements in a function as you want (as long as you make sure the stack is always balanced).

If you want to give an example of what you mean use pseudo code (or code in C, which can be pseudocode as well), since it's easier to express higher level concepts then.
AndyC
Dynamite Dan
Posts: 1445
Joined: Mon Nov 13, 2017 5:12 am

Re: fprwards and backwards logic in IF statements (in assembly)

Post by AndyC »

It's better to get out of the habit of thinking in BASIC and then trying to write that in assembly. Instead just think about what you need to do to accomplish the task in assembly directly.

Conditions are checked by setting flags. If you need the results later you can either save them somewhere or just re-do the test again. Re-reading a value from memory and doing another compare is a tiny operation compared to even decoding the "IF" statement in BASIC. Don't try to micro-optimise before you're comfortable coding (and thinking) in assembly.

Code: Select all

LD A, (value_to_check)
CP 6
CALL Z, value_is_6

LD A, (value_to_check)
CP 4
CALL Z, value_is_4

RET
is perfectly fine code when starting out.
User avatar
Seven.FFF
Manic Miner
Posts: 753
Joined: Sat Nov 25, 2017 10:50 pm
Location: USA

Re: fprwards and backwards logic in IF statements (in assembly)

Post by Seven.FFF »

If you want to call a routine which returns from the calling routine, you can pop and discard the first return address before returning.

Code: Select all

    call FirstRoutine
    ; SecondRoutine returns here

FirstRoutine:
    call SecondRoutine
    ; SecondRoutine does NOT return here
    ret
    
SecondRoutine:
    ; adjust the stack to return to the caller’s caller
    pop hl
    ret
You can also jump to the first routine instead of calling it, so the ret returns from the first routine.

Code: Select all

    call FirstRoutine
    ; SecondRoutine returns here

FirstRoutine:
    jp SecondRoutine
    ; SecondRoutine does NOT return here
    ret
    
SecondRoutine:
    ; return to the caller’s caller
    ret
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
NXtel NXTP ESP Update ESP Reset CSpect Plugins
User avatar
ParadigmShifter
Manic Miner
Posts: 951
Joined: Sat Sep 09, 2023 4:55 am

Re: fprwards and backwards logic in IF statements (in assembly)

Post by ParadigmShifter »

Wall_Axe wrote: Tue Oct 17, 2023 11:50 am Storing the location on a stack could be risky for creating bugs?
Using the machine stack (SP) can be tricky since it stores the return address on the stack but it is easy to write stack functions which do not use SP if you need an independent (or several) stacks.

e.g.

Code: Select all

stackworkspace BLOCK STACK_SIZE ; some assemblers use DEFS instead of BLOCK to reserve memory
MyStackPtr dw stackworkspace
; can have a count of stack current size if you want to or you can work it out on demand

; push DE to stack
push_to_mystack:
    ld hl, (MyStackPtr)
    ld (hl), e
    inc hl
    ld (hl), d
    inc hl
    ld (MyStackPtr), hl
    ret
    
; pop from stack into DE
pop_from_mystack:
    ld hl, (MyStackPtr)
    dec hl
    ld d, (hl); pop high byte first since we are moving stack pointer backwards in memory
    dec hl
    ld e, (hl)
    ld (MyStackPtr), hl
    ret
should work (untested). That stack grows up in memory unlike the machine stack which grows down (easily modified so it does that though).

With your own stack you can push 8 bit values as well (although you need to know how many bytes to pop which is why tagged data helps)
AndyC
Dynamite Dan
Posts: 1445
Joined: Mon Nov 13, 2017 5:12 am

Re: fprwards and backwards logic in IF statements (in assembly)

Post by AndyC »

Seven.FFF wrote: Tue Oct 17, 2023 12:04 pm If you want to call a routine which returns from the calling routine, you can pop and discard the first return address before returning.
You can, but don't.

When you're just starting out in assembly you will absolutely confuse the F out of yourself if you start manipulating the stack like this. It's a more advanced technique and really only necessary for squeezing every last drop out of CPU time.
User avatar
ParadigmShifter
Manic Miner
Posts: 951
Joined: Sat Sep 09, 2023 4:55 am

Re: fprwards and backwards logic in IF statements (in assembly)

Post by ParadigmShifter »

AndyC wrote: Tue Oct 17, 2023 12:10 pm You can, but don't.

When you're just starting out in assembly you will absolutely confuse the F out of yourself if you start manipulating the stack like this. It's a more advanced technique and really only necessary for squeezing every last drop out of CPU time.
I agree.

A good tip worth doing though is replace this

call func
ret

with this

jp func

which always works (as long as you don't try and later do something after the jp of course) :) I usually just comment out the call/ret combo when replacing it with a jp since then I know what my intention was.

Dropping into functions can be useful as well

setupforfunc: ; sometimes need to do this code e.g. initialisation, doing an expensive calculation we don't need to do if we already know the anwer etc.

; drop into the following func instead of returning

func:
; stuff
ret

e.g. I have drawsprite which calculates the screen address but another entry point drawspriteknowaddress which skips that part of the function, so drawsprite "drops into" drawspriteknowaddress once it has calculated the address
User avatar
Seven.FFF
Manic Miner
Posts: 753
Joined: Sat Nov 25, 2017 10:50 pm
Location: USA

Re: fprwards and backwards logic in IF statements (in assembly)

Post by Seven.FFF »

I think this has gone way past confusing the OP. He can just do a bunch of simple condition checks chained together as originally suggested,but he seems to want to make it more complicated and use the stack.
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
NXtel NXTP ESP Update ESP Reset CSpect Plugins
User avatar
ParadigmShifter
Manic Miner
Posts: 951
Joined: Sat Sep 09, 2023 4:55 am

Re: fprwards and backwards logic in IF statements (in assembly)

Post by ParadigmShifter »

I very very rarely do a double return by popping the return address of the calling function before returning but that is very rare (not done it in current code, I think I did it once in my console code stuff once only, can't remember why).

func1:
call func2
ret

func2:
call func3
ret

func3:
; return to func1 instead of func2
pop af ; pop return address into some register pair you don't care about (if you care about all registers, you can use INC SP twice but that is slower)
ret ; will return to func1 instead of func2

I wouldn't recommend that though in 99.99% of cases
Post Reply