Sigma Relocating modules ------------------------ *INSTALL loads and relocates a module at the top of memory A relocating module has two major parts. The code body and the relocation table. The module can be laid out in two ways. The relocation table can be before to code body, and so can be reclaimed after use, or it can be within or after the code body, where it cannot be reclaimed by the OS, but can be used as workspace by the module program itself. The way the OS recognises if the relocation table is before or after the start of the code is by looking at the first byte of the code. If it is a zero, then it marks the start of a pre-code relocation table. If it is a &18 byte, then it is a JR opcode, and marks the start of the code body. Any other values in the first location are currently invalid and will generate an error. If the first location is zero, indicating a pre-code relocation table, then the contents of the second location are ignored, and it is usually set to zero. The layout of a module is as follows: If there is a pre-code relocation table, then: 0000 00 00 DEFW 0000 ; marks a pre-code relocation table 0002 XX XX DEFW XXXX ; relocation addresses : : : nnnn 00 00 DEFW 0000 ; end of relocation table marker Following straight on from the end of the relocation table, or at the start if the relocation table is not pre-code, is the module header: 0000 18 XX JR entry ; new MEMTOP jump destination 0002 18 XX JR service ; initialisation entry 0004 XX XX DEFW xxxx ; relocation table address, or zero if none 0006 00 DEFB 00 ; offset of module title, or zero if none 0007 00 DEFB 00 ; currently unused 0008 ; start of program code If the module has a title it should be a zero terminated string. If there is a pre-code relocation table, then the relocation table address at location start+&0004 is ignored. If a relocation table address is given or there is a pre-code relocation table, then the OS relocates the module. The module should have been assembled to run at location zero, and the relocation table should contain the addresses of the absolute fields of instructions within the program, with a zero address terminating the table. The OS modifies these fields by adding the address of the real start to them. After relocation, a pre-code relocation table is reclaimed, whereas an in-code relocation table can be used as workspace by the module. After relocating, the OS calls the module at its service entry at start+0002 with A set to zero and HL set to the old MEMTOP value. This value should be placed somewhere so that calling the entry address at start+0000 daisy-chains to the next module. Any other initialisation should by done by the module at this point, eg intercepting vectors. The OS also puts a copy of old MEMTOP where the relocation table address used to be at location start+0004, just before performing the initialisation call. This link address is used for daisy-chaining through the modules and should not be modified. After this call, the MEMTOP pointer at location 0006/7 is set to point to the start of the module. Memory layout before and after INSTALLing a module with an in-code relocation table | | | | | | | | | | |/\/\/\/\/\/\/\/\/\/\/| | | | | | | | Stack copied to | | | | here | | | | | | | becomes new MEMTOP -> +---------------------+ | | | Entry points | | | +---------------------+ | | | | | | | | | | | Main Program Body | | | | | | | | | |/\/\/\/\/\/\/\/\/\/\/| +---------------------+ | | | Table of relocation | | Current | | addresses, used as | | Stack | | program workspace | | | | after relocation | +---------------------+ +---------------------+ | Current MEMTOP | | | Before INSTALLing After INSTALLing Memory layout before, during and after INSTALLing a module with a pre-code relocation table | | | | | | | | | | | | | | |/\/\/\/\/\/\/\/\/\/\| | | | | | | | | | | | Stack copied to | | | | | | here | |/\/\/\/\/\/\/\/\/\/\| | | | | | | | | +--------------------+ | Stack copied to | | | | Table of relocation| | here | | | | addresses | | | | | +--------------------+ +--------------------+ | Becomes new MEMTOP-|->| Entry points | | Entry points | | | +--------------------+ +--------------------+ |/\/\/\/\/\/\/\/\/\/\| | | | | | | | | | | | Current | | Main Program Body | | Main Program Body | | Stack | | | | | | | | | | | +--------------------+ +--------------------+ +--------------------+ | Current MEMTOP | | | | | Before INSTALLing During INSTALLing After INSTALLing Summary of service entry codes The OS 'talks' to modules by calling their service entry point at location start+0002. All modules MUST respond to the following three calls: A=0 Initialise Module This call is issued at installation with HL set to old MEMTOP, see above. This call is only issued once, and the code to deal with it can be replaced with a RET opcode, and the rest of it reclaimed by the module and used as workspace. A=&FF Kill Module This call is issued to kill the tail-end module. The module should restore ALL vectors that it modified and close down gracefully. The module MUST kill itself on this call, as the stack will be rewritten over it. A=&FE Request Module Shutdown ('Die, Please') This call is the same as with A=&FF, except the module can prevent itself being killed by returning A=0 All other values of A on entry are currently undefined, and should return with all the registers preserved (the flags are allowed to be modified). Any program can find out where it is currently executing by CALLing address &000E. This returns in the HL register the address of the location after the call. An example relocating module: 10 REM > ColourSrc 20 REM An example of a relocating module 30 REM (C) J.G.Harston 40 osbyte=&FFF4:oswrch=&FFEE:osnewl=&FFE7:PCtoHL=&E 50 pr2hex=&FFAD:pr_text=&FFB3:DIM mcode% 500 60 FOR pass%=4 TO 7 STEP 3 70 P%=0:O%=mcode% 80 [OPT pass% 90 JR entry 100 JR service 110 DEFW reloc_table:\ relocation table addr 120 DEFB title AND &FF:\ offset of title string 130 DEFB 0:\ empty 140 .title 150 DEFS "Coloured lines":DEFB 0 160 .entry 170 JP 0 180 .service 190 CP &FE:JR NC,servFE 200 CP 0:RET NZ:\ Not recognised, so exit 210 \ initialise 220 LD (entry+1),HL:.here0:\ Set up daisy chain 230 LD HL,(oswrch+1):LD (old_wrch+1),HL:.here1 240 LD HL,new_wrch:.here2 250 LD (oswrch+1),HL:\ Intercept oswrch 260 CALL pr_text 270 DEFS "Module loaded at &":DEFB 0 280 CALL PCtoHL:.label 290 LD DE,label:AND A 300 SBC HL,DE:\ HL now holds real addr of start 310 CALL pr2hex:\ This value will be the same as the new MEMTOP 320 JP osnewl:\ Finished 330 .servFE:\ Kill module 340 LD HL,(old_wrch+1):.here3 350 LD (oswrch+1),HL:\ Restore in one go, in case of interupts 360 RET:\ exit, OS will reclaim my corpse 370 \ 380 \ new oswrch routine 390 .new_wrch 400 PUSH AF:PUSH HL 410 LD A,135:CALL osbyte 420 LD A,H:CP 7:JR Z,wrch2:\ Screen is in MODE 7 430 POP HL 440 .old_wrch2 450 POP AF 460 .old_wrch 470 JP 0 480 .wrch2 490 POP HL:POP AF:PUSH AF 500 CP 13:JR Z,wrch_colour 510 CP 12:JR NZ,old_wrch2 520 .wrch_colour 530 CALL old_wrch:.here4 540 LD A,134:CALL oswrch 550 LD A,157:CALL oswrch 560 LD A,135:CALL oswrch:\ White text, blue background 570 POP AF 580 RET 590 .reloc_table 600 DEFW here0-2:DEFW here1-2:DEFW here2-2 610 DEFW here3-2:DEFW here4-2 620 DEFW 0 630 ]:NEXT 640 OSCLI "SAVE Colours "+STR$~mcode%+" "+STR$~O% This short module will make every line in MODE 7 be displayed with white characters on a blue background. Notice how the labels in the relocation table have two subtracted from them all. This is because the labels actually refer to the location after the absolute reference. Obviously, the automatic OS relocation cannot relocate absolute references where the value is split into to bytes, eg with the sequence: LD L,addr MOD 256 LD H,addr DIV 256 Here, the address is not held in two consecutive bytes, and cannot be relocated by the OS. The solution to this is either not use split-byte references, or write the module's initialisation routine so that it relocates them itself. Bear in mind, though, that the position in memory of the module can be anywhere, and not neccesarily on a page boundary. This makes it difficult to use split-byte references, and it would be recommended that this be steered clear of in writing a relocating module. Another point to recognise, is that even with the short module above, the relocation table already has five entries. With the lack of a PC relative addressing mode on the Z80, relocatable code is always a problem. Also, it is very easy to forget to mention one or two references, which would make the module completely unworkable. Unfortunately, there is no feature in the Basic assembler to allow the automatic creation of a relocation table, so you have to be very careful in writing a module to make sure you have a relocation table entry for every absolute reference. It is possible, though, to automate the generation of a relocation table.