From: Ullrich von Bassewitz (uz_at_musoftware.de)
Date: 2002-03-23 11:41:38
Hi! On Fri, Mar 22, 2002 at 03:51:16PM -0800, Adam Wozniak wrote: > I think I need a better understanding of how the cc65 parameter stack > relates to the 6502 call/return stack. > > Are these the same beast? Do they overlap? Or ar they completely separate? They are completely separate. > I've only got 128 bytes of real ram to work with, mapped at 0x80->0xFF > I've got some other areas (0x00->0x3F and 0x280->0x29F) which are memory > mapped devices. My ROM is 0xF000->0xFFFF. > > Where should the 6502 stack go? The parameter stack? You will become quite some problems with such a limited target system. The compiler and runtime needs 24 bytes in the zero page for itself (you may be able to strip that down to 18 bytes, but I cannot guarantee that this will work with new versions of the compiler). So you are left with 104 bytes for your data and both stacks. Assuming that you will use 10 nested levels of subroutine calls plus a few bytes for register saves inside these routines, plus 6 bytes for an interrupt handler, you will need about 30 bytes of call stack. The size of the parameter stack depends heavily on your code and setup. Let's assume you will use mostly global variables, and almost no parameters passed on the stack. This would mean that 30-50 bytes for the parameter stack should be ok for 10 nested subroutine levels. This sums up to 128 RAM bytes total - 24 Zero page usage of the compiler - 30 Call stack size - 30 Parameter stack size --------- 44 Bytes left for your program As you can see there is not much left. > MEMORY { > RAM: start = $80, size=$80, file="trash.bin"; > ROM: start = $F000, size=$1000, file="rom.bin"; > TIA: start = $00, size=$40, file="trash.bin"; > RIOT: start = $280, size=$20, file="trash.bin"; > } If you leave the file name empty (file=""), the data will be discarded and not written to any file. So there is no need to use a "trash" file. And %O is replaced by the output file name from the command line, so using MEMORY { RAM: start = $80, size=$80, file=""; ROM: start = $F000, size=$1000, file=%O; TIA: start = $00, size=$40, file=""; RIOT: start = $280, size=$20, file=""; } the target file name may be specified on the linker command line using the -o option. Adding the devices to the linker config is possible, but since the actual values are then unknown by the compiler, the compiler generated code is not as good as it could be. So I would suggest to use define these devices as memory mapped structures in a header file like this: struct RIOT_ { unsigned char a; /* Register a */ unsigned char b; /* Register b */ }; /* Map a RIOT structure to $280 */ #define RIOT (*(struct RIOT_*)0x280) This will allow you to use the RIOT as unsigned char x = RIOT.b; RIOT.a = 0x01; while the compiler still works with known target addresses. > SYMBOLS { > __STACKSIZE__ = $80; > } This symbol is unused in your code, so there is no need to define it. You may also need the FEATURE CONDES declarations depending on which library file you use. If in doubt, it is better to include it. > ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; > ;;;;;;;;;;; Is any of this right??? > > ; set the 6502 stack pointer to FF > ldx #$ff > txs > > ; set up the cc65 parameter stack > tsx > stx spsave Since your program will probably run forever, there is no need to save the current stackpointer, since you will never come back and restore it. > lda #<TOPMEM > sta sp > lda #>TOPMEM > sta sp+1 This would mean that your call stack lives at $E0-$FF, and below that, the parameter stack. Assuming the parameter stack will also have 32 bytes, the RAM left goes from $80 to $BF. This is the memory available for the RAM segment, so you have to change the linker config accordingly: MEMORY { RAM: start = $80, size=$40, file=""; ROM: start = $F000, size=$1000, file=%O; } You can also move the symbols for the stack sizes into the linker config, so you can change the stack sizes without changing your sources. Please note that you will have to adjust the size of the RAM segment manually when changing the stack sizes, the linker will not do that automatically. MEMORY { # The following three areas cover the $80-$FF range completely RAM: start = $80, size = $40, file = ""; PARMSTACK: start = $C0, size = $20, file = "", define = yes; CALLSTACK: start = $E0, size = $20, file = "", define = yes; # ROM: start = $F000, size = $1000, file=%O; } The following code will use this symbols: ; Import linker generated symbols .import __CALLSTACK_START__, __CALLSTACK_SIZE__; .import __PARMSTACK_START__, __PARMSTACK_SIZE__; ; Set the 6502 call stack as defined in the linker config ldx #<(__CALLSTACK_START__ + __CALLSTACK_SIZE__ - 1) txs ; Set the parameter stack as defined in the linker config lda #<(__PARMSTACK_START__ + __PARMSTACK_SIZE__) sta sp lda #>(__PARMSTACK_START__ + __PARMSTACK_SIZE__) sta sp+1 BTW: There are several ways to achieve the same result, you may also declare call and parameter stack as BSS style segments and map the into the RAM segment. The disadvantage is that the linker will emit a warning if none such segment is found, so you will have to generate it in an assembly file. The advantage is, that the linker will do all the needed size calculations for you. Regards Uz -- Ullrich von Bassewitz uz_at_musoftware.de ---------------------------------------------------------------------- To unsubscribe from the list send mail to majordomo_at_musoftware.de with the string "unsubscribe cc65" in the body(!) of the mail.
This archive was generated by hypermail 2.1.3 : 2002-03-23 11:41:58 CET