JamesD Posted August 9, 2015 Share Posted August 9, 2015 Let me ask you this... are you trying to do it all in C because you want to avoid assembly? Quote Link to comment Share on other sites More sharing options...
RXB Posted August 9, 2015 Share Posted August 9, 2015 C is a good language but a huge waste of memory to use. Unix when converted from Assembly to C took up so much more space that it took a cut down version call Linux to fit on a standard PC. Later with the marked increase in memory on Desktop PC Unix would fit, but with a huge cut down of Libraries. Assembly takes more time and effort to get it right. Quote Link to comment Share on other sites More sharing options...
JamesD Posted August 9, 2015 Share Posted August 9, 2015 To clarify, I was just referring to the bank switched function calls.But yeah, C takes less time to develop but wastes more memory and assembly takes longer to get right. 1 Quote Link to comment Share on other sites More sharing options...
RXB Posted August 9, 2015 Share Posted August 9, 2015 You know bank switching SAMS or ROMs is much more easy from GPL then any other way as you can switch the entire RAM without using any RAM? Quote Link to comment Share on other sites More sharing options...
JamesD Posted August 9, 2015 Share Posted August 9, 2015 ;Function stub routines ... ;Trampoline return code TrampolineExit: ;pop previous memory page from stack MOV R11,@R11Temp ;save R11 MOV @STACK,R11 ;get the stack pointer DECT R11 ;since stack points to next stack location MOV *R11-,@MEMPAGE ;get the previous memory page info ... There is no auto decrement in indirect addressing?Ok, so a minor code change. ;Trampoline return code ... TrampolineExit: ;pop previous memory page from stack MOV R11,@R11Temp ;save R11 MOV @STACK,R11 ;get the stack pointer DECT R11 ;since stack points to next stack location MOV *R11,@MEMPAGE ;get the previous memory page info DECT R11 ;decrement stack pointer ... Quote Link to comment Share on other sites More sharing options...
Tursi Posted August 9, 2015 Share Posted August 9, 2015 (edited) (Never mind, already covered, and no underscore on the 9900 GCC like I thought ) If you aren't familiar with 9900 assembly, and you aren't familiar with the 9900 port of GCC, I'd recommend building the switching in C first. You can always look at the generated assembly code and optimize that. The 9900 GCC usually produces pretty good code. Edited August 9, 2015 by Tursi Quote Link to comment Share on other sites More sharing options...
Tursi Posted August 9, 2015 Share Posted August 9, 2015 As a test, I wrote a simple trampoline function and verified the assembly -- you'll have a hard time doing much better, I think. void trampoline(void (*target)(), volatile char *targetBank) { volatile char *old = CurrentBank; // save the current bank CurrentBank = targetBank; // update the cache variable *targetBank; // force a memory read to switch, but we don't need the result. The volatile makes it work. target(); // call the target function CurrentBank = old; // update the cache variable *old; // force a memory read to switch back } This function takes the address of a function to call, and a pointer to the bank switch address (ie: 0x6000 for bank 0, 0x6002 for 1, etc). It expects that somewhere you have a global to cache the 'current' bank, defined as "volatile char *CurrentBank". Volatile is important to prevent optimizing out or re-ordering accesses to it. The generated assembly code for this function looks like this (using -O2, no optimizations produced broken code). def trampoline trampoline ai r10, >FFFC * update the stack pointer mov r11, *r10 * save return address on stack mov r9, @>2(r10) * save frame pointer on stack mov @CurrentBank, r9 * save current value of CurrentBank ('old') mov r2, @CurrentBank * save 'targetBank' into CurrentBank movb *r2, r2 * read targetBank, which performs the bank switch bl *r1 * call target function mov r9, @CurrentBank * restore saved value from 'old' (note: saved in register!) movb *r9, r1 * perform the memory read, which switches the bank back mov *r10+, r11 * restore return address from stack mov *r10+, r9 * restore frame pointer from stack b *r11 * return to caller A nice thing about this function is it's completely position independent. You could build it without any special consideration, and manually copy it from ROM to RAM or scratchpad (it's only 32 bytes) for actual execution. It'd be hard to do too much better - even without the GCC considerations, as a generic trampoline function I don't know if I'd change anything there. 2 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted August 9, 2015 Share Posted August 9, 2015 (edited) I make no promises on syntax but this is the general idea using a stack to track mem pages and return addresses.... and it's bigger than it probably needs to be. I'm sure an experienced 9900 programmer can improve on it ;Function stub routines Function1: MOV FunctionTableAddress1,@FunctionTemp JMP TrampolineMain ... Function2: MOV FunctionTableAddress2,@FunctionTemp JMP TrampolineMain ... ;Trampoline function call code TrampolineMain: MOV R11,@R11Temp ;save R11 to temp location MOV @STACK,R11 ;STACK points to next available location on the stack MOV @R11Temp,*R11+ ;push original R11 value to the stack ;push current memory page to stack MOV @MEMPAGE,*R11+ MOV R11,@STACK ;save the updated stack pointer ;load and set memory page of function from table based on value saved from R11 MOV @FunctionTemp,R11 ;point R11 to function table info MOV *R11+,@PageRegister ;set the memory page register MOV @PageRegister,@MEMPAGE ;copy for the return MOV *R11,@FunctionJump+2 ;modify the jump address MOV TrampolineExit,R11 ;Set return address to TrampolineExit FunctionJump: JMP >0000 ;jump to function, self modifying code changes it from >0000 ;Trampoline return code TrampolineExit: ;pop previous memory page from stack MOV R11,@R11Temp ;save R11 MOV @STACK,R11 ;get the stack pointer DECT R11 ;since stack points to next stack location MOV *R11-,@MEMPAGE ;get the previous memory page info ;set memory page MOV @MEMPAGE,@PageRegiser ;pop return address from stack MOV *R11,ReturnJump+2 ;self modifying code MOV @R11Temp,R11 ;restore R11 ReturnJump: JMP >0000 ;jump to return address I haven't tested this; but, something like the following should work: ; Use a register like R10 for stack pointer (SP), which we will point ; to the top item on the stack rather than the next available location. ; Stack will start at STACK and grow down to STACKEND, which programmer ; should probably check for overflow: ; LI SP,STACK ; If we grow the stack toward lower memory, it will be easier to push/pop items. ; Pushing involves 2 instructions: ; DECT SP ; reserve space ; MOV @AddressOfItem,*SP ; copy item to stack SP EQU R10 ; use SP (stack pointer) as a synonym for R10 STACKEND BSS 80 ; reserve what space is anticipated for maximum nesting of functions STACK LI SP,STACK ; initialize stack pointer Func1MemPage EQU >6000 ; page 0 for 378 latch Func2MemPage EQU >6002 ; page 1 for 378 latch ; Function table FUNTAB DATA Func1MemPage,Function1Address DATA Func2MemPage,Function2Address ;Function stub routines Function1 ; Function1Address is in bank 0 somewhere LI R11,FUNTAB MOV R11,@FunctionTemp BL @TrampolineMain ... Function2 ; Function2Address is in bank 1 somewhere LI R11,FUNTAB+4 MOV R11,@FunctionTemp BL @TrampolineMain ... ;Trampoline function call code in RAM TrampolineMain DECT SP ; reserve stack space MOV R11,*SP ; copy return address to stack ;push current memory page to stack DECT SP ; reserve stack space MOV @MEMPAGE,*SP ; save current memory page (if RAM, should point to modifiable RAM) ; ..if MEMPAGE is RAM, no bank change occurs...if there is a default ; ..bank, say DEFAULT EQU >6000, a return to RAM should probably be ; ..accompanied by a switch to that bank by setting MEMPAGE to DEFAULT ;load and set memory page of function from table based on value saved in FunctionTemp MOV @FunctionTemp,R11 ;point R11 to function table info MOV *R11+,R9 ; copy memory page and advance to function address CLR *R9 ; switch to bank for function MOV *R11,R9 ; copy function address BL *R9 ; branch & link to function ;Trampoline return code TrampolineExit ;pop previous memory page from stack MOV *SP,@MEMPAGE ; get return bank MOV *SP+,R9 ; pop memory page address CLR *R9 ; switch to return bank MOV *SP+,R9 ; pop return address B *R9 ; return to caller @Tursi's advice re C over Assembly is probably better, however. ...lee Edited August 9, 2015 by Lee Stewart Quote Link to comment Share on other sites More sharing options...
JamesD Posted August 9, 2015 Share Posted August 9, 2015 Nice to see that assembly. The 9900 does allow a couple things I thought it didn't because I couldn't find them in the book. I either missed the info in the book I was looking at or it just wasn't there.I initially had the stack building down and can't remember why I changed it.The C code definitely looks the best. Quote Link to comment Share on other sites More sharing options...
TheMole Posted August 9, 2015 Author Share Posted August 9, 2015 Unix when converted from Assembly to C took up so much more space that it took a cut down version call Linux to fit on a standard PC. Later with the marked increase in memory on Desktop PC Unix would fit, but with a huge cut down of Libraries. This is not true. AT&T put out UNIX for PCs in 1985, MINIX was release in 1987. Linux was first released on October 5th, 1991 because Linus wanted to have a free Unix-like OS for day-to-day use and MINIX's licensing conditions limited it to educational use. Nothing to do with memory consumption. Quote Link to comment Share on other sites More sharing options...
TheMole Posted August 9, 2015 Author Share Posted August 9, 2015 Ok but where is the bank switch and switch back so you can call between banks? If you bank switch there, you'll be in a different bank before your call. Just to clarify, I was working from your example code. That macro doesn't do the bank switching itself, that happens in the trampoline function. The macro only replace the stub functions in your example, not the actual trampoline code. If you are calling the trampoline code directly, it has to know what to set and call. That's what the "FunctionJump+2 = &_far_somefunction;" does, it overwrites the >0000 part in the JMP instruction in your code around line 26 with the address of _far_somefunction. The page can still be looked up in a lookup table, or you can simply follow the same approach as for the function pointer, with the exception that the page address needs to be hardcoded. Quote Link to comment Share on other sites More sharing options...
TheMole Posted August 9, 2015 Author Share Posted August 9, 2015 As a test, I wrote a simple trampoline function and verified the assembly -- you'll have a hard time doing much better, I think. void trampoline(void (*target)(), volatile char *targetBank) { volatile char *old = CurrentBank; // save the current bank CurrentBank = targetBank; // update the cache variable *targetBank; // force a memory read to switch, but we don't need the result. The volatile makes it work. target(); // call the target function CurrentBank = old; // update the cache variable *old; // force a memory read to switch back } This function takes the address of a function to call, and a pointer to the bank switch address (ie: 0x6000 for bank 0, 0x6002 for 1, etc). It expects that somewhere you have a global to cache the 'current' bank, defined as "volatile char *CurrentBank". Volatile is important to prevent optimizing out or re-ordering accesses to it. The generated assembly code for this function looks like this (using -O2, no optimizations produced broken code). def trampoline trampoline ai r10, >FFFC * update the stack pointer mov r11, *r10 * save return address on stack mov r9, @>2(r10) * save frame pointer on stack mov @CurrentBank, r9 * save current value of CurrentBank ('old') mov r2, @CurrentBank * save 'targetBank' into CurrentBank movb *r2, r2 * read targetBank, which performs the bank switch bl *r1 * call target function mov r9, @CurrentBank * restore saved value from 'old' (note: saved in register!) movb *r9, r1 * perform the memory read, which switches the bank back mov *r10+, r11 * restore return address from stack mov *r10+, r9 * restore frame pointer from stack b *r11 * return to caller A nice thing about this function is it's completely position independent. You could build it without any special consideration, and manually copy it from ROM to RAM or scratchpad (it's only 32 bytes) for actual execution. It'd be hard to do too much better - even without the GCC considerations, as a generic trampoline function I don't know if I'd change anything there. That's what I did for my first test, but I don't see an obvious way to make this work with functions that take arguments (except writing a specific trampoline function for each)? Quote Link to comment Share on other sites More sharing options...
Tursi Posted August 9, 2015 Share Posted August 9, 2015 That's what I did for my first test, but I don't see an obvious way to make this work with functions that take arguments (except writing a specific trampoline function for each)? Yes. At 40 bytes each, I think you'll have to work pretty hard to break the bank. Remember that calling convention is part of the compilation, if you try to abstract that all away, even if you manage to build something that works, it will reduce the compiler's ability to create optimized code. Since neither the compiler nor the linker are aware of banking right now, we don't get any specific optimizations tied to that, so not everything is going to be beautiful. For my own use I write a trampoline for every function that needs one, just to save the function call and parameter passing overhead. They're cheap. 1 Quote Link to comment Share on other sites More sharing options...
JamesD Posted August 9, 2015 Share Posted August 9, 2015 (edited) So, what we have here is a rewrite with stack that builds down and instructions I think I learned from you guys.Insert it could be completely crap disclaimer here. I removed so much code that I'm worried. ;Function stub routines Function1: MOV FunctionTableAddress1,@FunctionTemp B @TrampolineMain FunctionTableAddress1: DATA Func1MemPage,Function1Address ... Function2: MOV FunctionTableAddress2,@FunctionTemp B @TrampolineMain FunctionTableAddress2: DATA Func2MemPage,Function2Address ... ;Trampoline function call code TrampolineMain: MOV R11,*STACK ;push original R11 value to the stack DECT @STACK ;push current memory page to stack MOV @MEMPAGE,*STACK ;save the last memory page DECT @STACK ;load and set memory page of function from table based on value saved from R11 MOV @FunctionTemp,*R11 ;Point to table entry MOV *R11,@PageRegister ;set memory page register MOV *R11+,@MEMPAGE ;copy for the return BL *R11 ;call the function ;Trampoline return code ;pop previous memory page from stack INCT @STACK MOV *STACK,@MEMPAGE ;get the previous memory page info ;set memory page MOV @MEMPAGE,@PageRegiser ;pop return address from stack INCT @STACK MOV *STACK,R11 ;restore R11 B *R11 ;return to caller *edit*Technically, there doesn't need to be a function table. The data could be located with each stub routine itself. (updated code with this)I feel like I'm wandering around in the dark. Edited August 9, 2015 by JamesD Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted August 9, 2015 Share Posted August 9, 2015 (edited) So, what we have here is a rewrite with stack that builds down and instructions I think I learned from you guys. Insert it could be completely crap disclaimer here. I removed so much code that I'm worried. ;Function stub routines Function1: MOV FunctionTableAddress1,@FunctionTemp B @TrampolineMain DATA Func1MemPage,Function1Address ... Function2: MOV FunctionTableAddress2,@FunctionTemp B @TrampolineMain DATA Func2MemPage,Function2Address ... ;Trampoline function call code TrampolineMain: MOV R11,*STACK ;push original R11 value to the stack DECT @STACK ;push current memory page to stack MOV @MEMPAGE,*STACK ;save the updated stack pointer DECT @STACK ;load and set memory page of function from table based on value saved from R11 MOV @FunctionTemp,*R11 ;Point to table entry MOV *R11,@PageRegister ;set memory page register MOV *R11+,@MEMPAGE ;copy for the return BL *R11 ;call the function ;Trampoline return code ;pop previous memory page from stack INCT @STACK MOV *STACK,@MEMPAGE ;get the previous memory page info ;set memory page MOV @MEMPAGE,@PageRegiser ;pop return address from stack INCT @STACK MOV *STACK,R11 ;restore R11 B *R11 ;return to caller *edit* Technically, there doesn't need to be a function table. The data could be located with each stub routine itself. (updated code with this) I feel like I'm wandering around in the dark. I learned something new: I was going to tell you not to use the trailing ‘:’ in your labels, but Asm994a allows it and ignores it. I am pretty sure you cannot use it with the TI Editor/Assembler, however; but, there, you would be limited to 6 characters! In your branches to TrampolineMain, you need to use BL @TrampolineMain to have a return address put in R11. B is an unconditional branch, much like JMP, but unrestricted by distance—also, it is slower. Also, if a function stub is called by BL, you will need to preserve R11 before calling the trampoline code. With the BL change and your inclusion of the table information in the function stub, you no longer need the opening MOV because R11 is pointing at the DATA statement. I think that the stack pointer for a downward-growing stack should point to the top of the stack, not beyond it, which means that you must reserve that space with the DECT before copying a value to it. Also, STACK must be a register to use indirection. If you use R10, you will need to EQUate STACK to R10: STACK EQU R10. If you label the base of the stack as STACK0, say, you will need to load that value into the STACK pointer register before its first use or when you wish to clear the stack: LI STACK,STACK0. DECT @STACK should be DECT STACK and before the MOV. Popping the stack, then, is only one statement: MOV *STACK+,@MEMPAGE, for example. Another note: You do not want to return to the function stub at the DATA statement, which is where R11 is pointing upon entry into the trampoline code; so, I would decrement the stack pointer by 4 and store the current MEMPAGE value as the top of the two reserved spaces on the stack: AI STACK,-4 MOV @MEMPAGE,*STACK and wait until after the function table values are saved and R11 is incremented to the proper return address to store it under the old MEMPAGE value on the stack: ;load and set memory page of function from table based on value saved from R11 MOV *R11,@PageRegister ;set memory page register to table entry MOV *R11+,@MEMPAGE ;copy for the return MOV *R11+,R9 ;save function address; R11 now pointing to proper return address MOV R11,@2(STACK) ;store function stub return address under top stack item BL *R9 ;call the function One last note: In order to switch ROM banks, you need to write to the index location for a particular bank, which means that all of the memory page locations should be one of these indices. For a ROM using a 378 latch (non-inverted), the index location for switching to bank 0 is >6000; for bank 1, >6002, etc. I think you will need to use a register to do this. You should be able to do this by changing the page register code above to MOV *R11,R9 ;move ROM address index to R9 CLR *R9 ;switch to ROM bank address index in R9 and the trampoline return code to ;Trampoline return code ;set memory page, which should be a ROM bank index address MOV *STACK,R9 ;move ROM address index location to R9 CLR *R9 ;switch to ROM bank address index in R9 ;pop previous memory page from stack MOV *STACK+,@MEMPAGE ;get the previous memory page info ;pop return address from stack MOV *STACK+,R9 B *R9 ;return to caller ...lee Edited August 2, 2016 by Lee Stewart Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted August 9, 2015 Share Posted August 9, 2015 My bank-changing code in the last and a previous post won't work without another level of indirection, which I will correct in those posts, shortly. ...lee Quote Link to comment Share on other sites More sharing options...
JamesD Posted August 9, 2015 Share Posted August 9, 2015 I learned something new: I was going to tell you not to use the trailing : in your labels, but Asm994a allows it and ignores it. I am pretty sure you cannot use it with the TI Editor/Assembler, however; but, there, you would be limited to 6 characters!I think every other assembler I've used supports it. I'm not sure they require it though so I'm not sure why I adopted the practice. In your branches to TrampolineMain, you need to use BL @TrampolineMain to have a return address put in R11. B is an unconditional branch, much like JMP, but unrestricted by distancealso, it is slower. Also, if a function stub is called by BL, you will need to preserve R11 before calling the trampoline code.Doesn't the compiler BL to the code stubs? R11 should already have the return address of the caller so just save it and restore it on exit. If you use a BL from the code stubs you need to return from the bottom of the stub instead of the trampoline exit which is why I used B rather than BL. Or maybe I'm missing something. With the BL change and your inclusion of the table information in the function stub, you no longer need the opening MOV because R11 is pointing at the DATA statement.You need to save the return address of the caller in it's place but yeah, this would work. See my response on the stack below. I think that the stack pointer for a downward-growing stack should point to the top of the stack, not beyond it, which means that you must reserve that space with the DECT before copying a value to it. Also, STACK must be a register to use indirection. If you use R10, you will need to EQUate STACK to R10: STACK EQU R10. If you label the base of the stack as STACK0, say, you will need to load that value into the STACK pointer register before its first use or when you wish to clear the stack: LI STACK,STACK0. DECT @STACK should be DECT STACK and before the MOV. Popping the stack, then, is only one statement: MOV *STACK+,@MEMPAGE, for example.If you take the approach you suggest by using BL (which looks like an excellent idea the more I think about it), you do. You can push directly to the stack to save R11 in the stubs. Another note: You do not want to return to the function stub at the DATA statement, which is where R11 is pointing upon entry into the trampoline code; so, I would decrement the stack pointer by 4 and store the current MEMPAGE value as the top of the two reserved spaces on the stack: That's why I used B rather than BL in the stubs. AI STACK,-4 MOV @MEMPAGE,*STACK and wait until after the function table values are saved and R11 is incremented to the proper return address to store it under the old MEMPAGE value on the stack: ;load and set memory page of function from table based on value saved from R11 MOV *R11,@PageRegister ;set memory page register to table entry MOV *R11+,@MEMPAGE ;copy for the return MOV *R11+,R9 ;save function address; R11 now pointing to proper return address MOV R11,@2(STACK) ;store function stub return address under top stack item BL *R9 ;call the function One last note: In order to switch ROM banks, you need to write to the index location for a particular bank, which means that all of the memory page locations should be one of these indices. For a ROM using a 378 latch (non-inverted), the index location for switching to bank 0 is >6000; for bank 1, >6002, etc. I think you will need to use a register to do this. You should be able to do this by changing the page register code above to MOV *R11,*R11 ;set memory page register to table entry I don't see how this is setting the memory page. You are moving from/to the same location. You should be moving the page number to the page register of the hardware. If *R11 points to the same number it sure doesn't point the the paging hardware unless you expand the data and use MOV *R11+,*R11 . Or am I wrong about what that does? and the trampoline return code to ;Trampoline return code ;set memory page MOV *STACK,*STACK ;pop previous memory page from stack MOV *STACK+,@MEMPAGE ;get the previous memory page info ;pop return address from stack, while returning to caller B *STACK+ ;return to caller ...lee *STACK *STACK? I still don't see a memory page register being set if there is a bank register and parameters. So, I guess I don't understand what is going on there. The *STACK+ does work if you point to the current location so that's a good argument for that approach but you still need to save the return address in R11 to get back to the caller. B *STACK will save code even on what I have. Quote Link to comment Share on other sites More sharing options...
JamesD Posted August 9, 2015 Share Posted August 9, 2015 (edited) Ok, without pointing to the current stack address, I have this and it would get shorter with the changes *R11,*R11 for setting the memory page but I don't see how that works yet. ;Function stub routines Function1: ;push original return address to the stack MOV R11,*STACK BL @TrampolineMain DATA Func1MemPage,Function1Address ... Function2: ;push original return address to the stack MOV R11,*STACK BL @TrampolineMain DATA Func2MemPage,Function2Address ... ;Trampoline function call code TrampolineMain: ;finish the push DECT @STACK ;push current memory page to stack MOV @MEMPAGE,*STACK ;save the last memory page DECT @STACK ;load and set memory page of function from table based on value in R11 which points to the data MOV *R11,@PageRegister ;set memory page register MOV *R11+,@MEMPAGE ;copy for the return BL *R11 ;call the function ;Trampoline return code ;pop previous memory page from stack INCT @STACK MOV *STACK+,@MEMPAGE ;get the previous memory page info and adjust stack for return ;set memory page MOV @MEMPAGE,@PageRegiser ;return via address from stack B *STACK ;return to caller Edited August 9, 2015 by JamesD Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted August 9, 2015 Share Posted August 9, 2015 ... Doesn't the compiler BL to the code stubs? R11 should already have the return address of the caller so just save it and restore it on exit. If you use a BL from the code stubs you need to return from the bottom of the stub instead of the trampoline exit which is why I used B rather than BL. Or maybe I'm missing something. I am looking at this as strictly ALC. Whether or not this is the result of compiled code, if BL is used to execute the function stub, you would need to save R11 in the stub before moving on, just as you indicate. If you take the approach you suggest by using BL (which looks like an excellent idea the more I think about it), you do. You can push directly to the stack to save R11 in the stubs. Yup. That's why I used B rather than BL in the stubs. I thought as much, but did not want to presume anything. I don't see how this is setting the memory page. You are moving from/to the same location. You should be moving the page number to the page register of the hardware. If *R11 points to the same number it sure doesn't point the the paging hardware unless you expand the data and use MOV *R11+,*R11 . Or am I wrong about what that does? *STACK *STACK? I still don't see a memory page register being set if there is a bank register and parameters. So, I guess I don't understand what is going on there. The *STACK+ does work if you point to the current location so that's a good argument for that approach but you still need to save the return address in R11 to get back to the caller. B *STACK will save code even on what I have. This stuff is wrong because I needed another level of indirection with some of it—sorry about that. I corrected that post and will also correct the previous one soon. Regarding switching banks, there is no register to do that for ROM banks. You must write (MOV, CLR, etc.) to an index address, even though nothing changes because it is ROM. The index addresses are (for 378-latched ROM memory banks) >6000 for bank 0, >6002 for bank 1, etc. ...lee Quote Link to comment Share on other sites More sharing options...
JamesD Posted August 9, 2015 Share Posted August 9, 2015 (edited) Is this a legal instruction? MOV *STACK(4),R11 It's not in the book, it only shows something like @LABEL(5)*edit*Here is the corrected memory paging. (?) ;Function stub routines Function1: ;push original return address to the stack MOV R11,*STACK BL @TrampolineMain DATA Func1MemPage,Function1Address ... Function2: ;push original return address to the stack MOV R11,*STACK BL @TrampolineMain DATA Func2MemPage,Function2Address ... ;Trampoline function call code TrampolineMain: ;finish the push DECT @STACK ;push current memory page to stack MOV @MEMPAGE,*STACK ;save the last memory page DECT @STACK ;load and set memory page of function from table based on value in R11 which points to the data MOV *R11,R11 ;load the page value in R11 MOV R11,@MEMPAGE ;copy for the return CLR *R11 ;set memory page ;set up and call the function MOV *STACK(4),R11 ;for if the previous line doesn't work ; MOV @STACK,R11 ; A 4,R11 BL *R11 ;call the function ;Trampoline return code ;pop previous memory page from stack INCT @STACK MOV *STACK+,@MEMPAGE ;get the previous memory page info and adjust stack for return ;set memory page MOV @MEMPAGE,R11 CLR *R11 ;return via address from stack B *STACK ;return to caller Edited August 9, 2015 by JamesD Quote Link to comment Share on other sites More sharing options...
+mizapf Posted August 9, 2015 Share Posted August 9, 2015 No, it's not legal. After * there must be a number from 0 to 15, possibly followed by a plus sign. The number may also come from some explicit EQU lines (or implicit lines like R0 EQU 0, R1 EQU 1 ...). Even when STACK is a EQU constant, the parentheses are not allowed. Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted August 9, 2015 Share Posted August 9, 2015 What you want is MOV @4(STACK),R11 This, of course, assumes STACK is a synonym for a register number and not a memory address. Also, your DECT and INCT statements are decrementing/incrementing memory address contents, but the statements with *STACK are attempting to reference a register, which must evaluate to a number from 0 to 15. ...lee Quote Link to comment Share on other sites More sharing options...
JamesD Posted August 9, 2015 Share Posted August 9, 2015 (edited) And back to the drawing board. If *STACK is a register the code is totally wrong.Using another register is possible but you have to use one the compiler doesn't care about or preserve it. Edited August 9, 2015 by JamesD Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted August 10, 2015 Share Posted August 10, 2015 And back to the drawing board. If *STACK is a register the code is totally wrong. Using another register is possible but you have to use one the compiler doesn't care about or preserve it. Ah-h-h...I am flying blind when it comes to what registers the compiler may be using. Like I said, I was treating it as pure ALC...sorry. I suppose we could figure out a way to use our own registers for our own parameter stack and, if necessary, our own return stack, with LWPI or BLWP/RTWP for functions needing bank switching. Since we would likely need to use our stack for passing parameters to the function stub, we might need to switch to our workspace registers with LWPI to manage our stack(s), switch back to the system workspace and execute BLWP to get to the function stub with our own workspace with system workspace stuff in our R13 – R15 until we return to the caller with RTWP from the trampoline function. There's probably a less convoluted way to do this than my rambling here; but, this maybe will generate some ideas that actually might work.. ...lee Quote Link to comment Share on other sites More sharing options...
Tursi Posted August 10, 2015 Share Posted August 10, 2015 Not sound petty, but I posted working code. What are you guys trying to build? The compiler already maintains a stack.... it's overkill to create your own off to the side. If you must push your data to a stack (and you don't always have to, which was why the code GCC produced was nice, it would only store the bank and return address on the stack if it was necessary), if you must, just use the compiler's stack. it's pointed to by R10, counts down, and you have to pre-decrement it. Fix it back up before returning to compiled code, and you're golden. The compiler will store the function arguments in registers whenever possible -- in the case of the code I posted, R1 and R2 already have the arguments. This is faster and easier than trying to store the information in DATA statements after the call, especially if most of your code is already in C. The 9900 is not a stack-based processor, and Insomnia did a fantastic job adapting GCC to produce good code for it by not using the stack whenever it was possible - this is counter-intuitive if you are used to stack-based systems like the 6502 or Z80. It's not necessary to try to outsmart it - with twenty years experience there are still instances where it surprises me with brilliant assembly. I'm happy to go away and let you guys play with it if you're just trying to think it through, I don't mean to belittle that effort. I'd just like to help and I don't understand the end goal. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.