flashjazzcat Posted December 29, 2008 Share Posted December 29, 2008 What's generally the best way of doing this? Obviously it's a common technique on the Atari XL to place programs in the shadow RAM under the OS at $C000-$FFFF, and Turbo Basic, SpartaDOS, and dozens of other programs do it. But surely the main 'loop' of an application must reside in conventional RAM and call routines under the OS ROM after first disabling interrupts, etc. I've been trying to adapt an existing program to run under the OS ROM but it's proving a headache. Clearly anything that calls the CIO or re-enables the interrupts must reside outside the OS region and enable the OS ROM beforehand. I've tried disassembling Turbo Basic for some ideas but it's kind of daunting without any comments... Quote Link to comment Share on other sites More sharing options...
drac030 Posted December 29, 2008 Share Posted December 29, 2008 Clearly anything that (...) re-enables the interrupts must reside outside the OS region and enable the OS ROM beforehand. Not necessarily. You can place own vector at the NMIVEC, that points to a RAM routine, which enables the OS-ROM, calls the (f.e.) VBL routine, disables the ROM upon return, and gets back to the program. This involves storing a fake return address onto the stack, but will work, and you can have both the code under the ROM plus system interrupt routines enabled. Quote Link to comment Share on other sites More sharing options...
flashjazzcat Posted December 29, 2008 Author Share Posted December 29, 2008 Not necessarily. You can place own vector at the NMIVEC, that points to a RAM routine, which enables the OS-ROM, calls the (f.e.) VBL routine, disables the ROM upon return, and gets back to the program. This involves storing a fake return address onto the stack, but will work, and you can have both the code under the ROM plus system interrupt routines enabled. Excellent, thank you. It's so easy when someone tells you how I'd looked into this ten or more years ago and it seems so obvious now you've explained it. The CIO gets handled in a similar way: all JSRs to CIOV are simply re-routed to a routine which sets bit 0 of PORTB, calls the real CIOV, then disables the OS again and returns to the main program. Great... Quote Link to comment Share on other sites More sharing options...
thorfdbg Posted December 29, 2008 Share Posted December 29, 2008 What's generally the best way of doing this? Obviously it's a common technique on the Atari XL to place programs in the shadow RAM under the OS at $C000-$FFFF, and Turbo Basic, SpartaDOS, and dozens of other programs do it. But surely the main 'loop' of an application must reside in conventional RAM and call routines under the OS ROM after first disabling interrupts, etc. I've been trying to adapt an existing program to run under the OS ROM but it's proving a headache. Clearly anything that calls the CIO or re-enables the interrupts must reside outside the OS region and enable the OS ROM beforehand. I've tried disassembling Turbo Basic for some ideas but it's kind of daunting without any comments... It depends on what the program attempts to do. If it doesn't require the Os, the easiest is just to replace the Os completey - a couple of demos work this way. Programs that do use the Os consists of two parts, one that is placed below the Os ROM, and a second in conventional RAM that is responsible for switching back and forth. Usually, the Os ROM is in use, but when a routine from the extended part is required, the ROM is disabled. Typically, Os code is not duplicated, but instead pseudo-call-ins in the conventional RAM block are created. They first enable the ROM, then jump into the ROM space, then disable the ROM again. A couple of difficulties remain. First, interrupts will of course happen no matter whether the Os is enabled or disabled, thus interrupt code must be there. VBI and IRQs are usually handled by first re-enabling the Os, then calling the Os routine. DLIs are more critically, and usually jumped into directly, without re-enabling the Os. This is somewhat critical since a DLI might in principle reside in the Os ROM. For example, the XL rom "smooth scrolling" code is DLI driven. Turbo-BASIC does not keep care of this and crashes if a smooth-scrolling editing screen is created. A second complication is that the character ROM (i.e. the standard set at 0xe000 and the international set at 0xcc00) must be duplicated, you'd otherwise see flickering or distortion whenever the Os is not mapped in. Turbo-Basic re-enables the Os for all operations that might potentially call the Os, for example the USR function first enables it before the user subroutine is called. Floating point routines are, of course, not duplicated. If you care, I could dig out my Dos 2.XL which also resided under the Os ROM - it did a couple of additional tricks, the disk buffers were also placed under the Os, thus it only required like ~170 bytes of precious conventional RAM space, all only for the jump-ins and interrupt-handling. Greetings, Thomas Quote Link to comment Share on other sites More sharing options...
tjb Posted December 29, 2008 Share Posted December 29, 2008 Can someone post some code to move the OS into RAM and then switch to it? I'd like to use the unused 4K block at $C000 as well but I'd also like to replace the ROM character set with one of my own. Maybe Heaven could chime in? I seem to recall looking over some code of his and seeing a routine that does this. tjb Quote Link to comment Share on other sites More sharing options...
Heaven/TQA Posted December 29, 2008 Share Posted December 29, 2008 here is my routine... (similar to the one we used in Numen): switch_off_os_rom sei;stop interrups lda #0 sta $d40e lda #$fe sta $d301 lda #<nmi sta $fffa sta $fffe lda #>nmi sta $fffb sta $ffff lda #$c0 sta $d40e rts ; NMI routine ; ^4f==#$5f if VBLKI ; ^4f==#$9f if DLI nmi bit $d40f bpl sys_vbl ; DLI routine, e.g. ; pha jmp (dliv) ; pla ; rti ; VBL routine, e.g. sys_vbl pha txa pha tya pha dec 20 jsr vbl lda dlistv sta $d402 lda dlistv+1 sta $d403 pla tay pla tax pla rti so I do this: jsr switch_off_os_rom and aftter that the OS is gone... but beware...you need to take care for everything now... so...no shadow registers etc available. pure Atari hardware only... Quote Link to comment Share on other sites More sharing options...
Heaven/TQA Posted December 29, 2008 Share Posted December 29, 2008 OS copy... check out: http://atariwiki.strotmann.de/xwiki/bin/vi...y+OS+ROM+to+RAM Quote Link to comment Share on other sites More sharing options...
Heaven/TQA Posted December 29, 2008 Share Posted December 29, 2008 but remember using only $c000 ff. will cause problems, too... as important OS routines are there even if you are only going through the vector table ($e4xx). so... I would switch OS completly off.. Quote Link to comment Share on other sites More sharing options...
Heaven/TQA Posted December 29, 2008 Share Posted December 29, 2008 btw.... I drove the guys at #c-64 crazy when asking them how can I switch the OS off and use the ram below... on C64 you simply write to ROM area and stores in that area just will guided through the shadow ram below... that is cool...without any additional code... of course for reading you have to switch off the OS ($00,$01 ZP) but you can put graphics stuff for the VIC there... without switching OS off... not bad feature, too... Quote Link to comment Share on other sites More sharing options...
Rybags Posted December 30, 2008 Share Posted December 30, 2008 If you wanted to, you could "fudge" the handling of Interrupts. For an NMI, just handle the DLIs yourself since it's only a couple of instructions. For VBlank, you could put a return address and processor status onto the stack so that your code gets control back. So, the flow would be: Is it DLI? If Yes, JMP via ($200) Save registers on stack. Switch OS back in. Retrieve the pre-VBlank Processor status value from the stack. This is needed so that the Interrupt mask status bit can be read correctly by the OS routine before Stage 2. Push Processor Status and our return address onto stack. CLD, push registers onto stack (these will be discarded later). JMP via ($222) <normal OS VBlank processing now takes place with ROM switched in> You will then have control back when the normal sequence pulls registers and does an RTI. Then all you need to do is switch the OS ROM back out, and return from the Interrupt as normal (pull regs, RTI). You could use a similar technique for IRQs. Quote Link to comment Share on other sites More sharing options...
atariksi Posted December 30, 2008 Share Posted December 30, 2008 but remember using only $c000 ff. will cause problems, too... as important OS routines are there even if you are only going through the vector table ($e4xx). so... I would switch OS completly off.. Unless he puts old OS in, then he has the $C000...$CFFF as free RAM. Or he can over-write the international charset w/code as there's 1024 bytes there. Quote Link to comment Share on other sites More sharing options...
flashjazzcat Posted December 30, 2008 Author Share Posted December 30, 2008 If you care, I could dig out my Dos 2.XL which also resided under the Os ROM - it did a couple of additional tricks, the disk buffers were also placed under the Os, thus it only required like ~170 bytes of precious conventional RAM space, all only for the jump-ins and interrupt-handling. Some commented source code for a real application that resides under the OS would be worth studying. This discussion is raising some very interesting points. If you wanted to, you could "fudge" the handling of Interrupts. For an NMI, just handle the DLIs yourself since it's only a couple of instructions. For VBlank, you could put a return address and processor status onto the stack so that your code gets control back. So, the flow would be: Is it DLI? If Yes, JMP via ($200) Save registers on stack. Switch OS back in. Retrieve the pre-VBlank Processor status value from the stack. This is needed so that the Interrupt mask status bit can be read correctly by the OS routine before Stage 2. Push Processor Status and our return address onto stack. CLD, push registers onto stack (these will be discarded later). JMP via ($222) <normal OS VBlank processing now takes place with ROM switched in> You will then have control back when the normal sequence pulls registers and does an RTI. Then all you need to do is switch the OS ROM back out, and return from the Interrupt as normal (pull regs, RTI). You could use a similar technique for IRQs. This is the system I'm going to adopt. For those interested, it will result in a variant of my word processor with a 24K main text buffer, more macro space, etc. Or he can over-write the international charset w/code as there's 1024 bytes there. I can use the space occupied by both character sets since the program only accesses them in order to copy the font into its own buffer in main memory (where it can be modified or replaced entirely by a user-defined font). Quote Link to comment Share on other sites More sharing options...
Rybags Posted December 30, 2008 Share Posted December 30, 2008 I should have added: You'd probably also want to cover entry points for the OS routines around $E450... and the Floating Point routines (so BASIC can work normally). Additionally, the CIO device handler tables since BASIC and some other apps can use the Put Character routine directly. For the OS entry points, you could probably have a single routine that takes care of all of them - just change the JMP entries to JSR. The routine could then deduce what's being called by pulling the return address from the stack. So, to summarise - a RAM based OS "shell" is doable although some patching is needed and the RAM available to the programmer might be a bit fragmented. Also, programs would run a touch slower since services have to be intercepted, the normal OS switched in/out etc. Quote Link to comment Share on other sites More sharing options...
tjb Posted December 30, 2008 Share Posted December 30, 2008 but remember using only $c000 ff. will cause problems, too... as important OS routines are there even if you are only going through the vector table ($e4xx). so... I would switch OS completly off.. Thanks for the reply and code! I tend to agree. I will likely just shut the OS off. I'm really not using it with the exception of some of the shadow registers. I guess I was thinking I'd run some code as part of the binary load process that would copy the OS to Ram and then switch to the Ram version and then load my modified character set at the same spot as the normal one. Finally I'd like to put sprite data in the 4K $C000 area. It almost seems like I would have to use a Ram OS version if I intend to load data into these areas from disk (so CIO is still around). tjb Quote Link to comment Share on other sites More sharing options...
Rybags Posted December 30, 2008 Share Posted December 30, 2008 The way to go is load it low, then have an Init routine that relocates it under the OS. I've noticed that for some reason, Atari800Win+ will allow "Load Executable" into the RAM under ROM. On a real machine, you can't write through to RAM like on the C-64. Quote Link to comment Share on other sites More sharing options...
Heaven/TQA Posted December 30, 2008 Share Posted December 30, 2008 in the past I have loaded the stuff to main ram, switched off rom and then moved the stuff over... or I used the XBOOT utility with the /P flag (professional loader) which can load stuff directly $0000-$ffff Quote Link to comment Share on other sites More sharing options...
flashjazzcat Posted December 30, 2008 Author Share Posted December 30, 2008 (edited) Can anyone explain what the access to the stack is for in the Turbo Basic XL NMI handler? I've decided to adapt this code but it's giving me trouble. handler: bit NMIST bpl not_dli jmp (VDSLST) not_dli: pha txa pha lda #high exit_int pha lda #low exit_int pha tsx lda $105,X pha cld pha txa pha tya pha inc PORTB sta NMIRES jmp (VVBLKI) exit_int: pla tax dec PORTB pla rti I mean the "LDA $0105,X" part... Edited December 30, 2008 by flashjazzcat Quote Link to comment Share on other sites More sharing options...
Rybags Posted December 30, 2008 Share Posted December 30, 2008 That would be the "P" status from when the NMI occurred. Since it's setting up a dummy interrupt stack entry to get control back, it's ensuring the "P" entry is the same. The OS needs it because it checks it before Stage 2 - it skips Stage 2 if the IRQ mask is set. Quote Link to comment Share on other sites More sharing options...
flashjazzcat Posted December 30, 2008 Author Share Posted December 30, 2008 That would be the "P" status from when the NMI occurred. Since it's setting up a dummy interrupt stack entry to get control back, it's ensuring the "P" entry is the same. The OS needs it because it checks it before Stage 2 - it skips Stage 2 if the IRQ mask is set. Thanks for the quick answer. My problems must lie elsewhere! Quote Link to comment Share on other sites More sharing options...
Rybags Posted December 30, 2008 Share Posted December 30, 2008 Also note it's doing INC/DEC of PORTB. That will toggle whether the OS is in or out, so you have to ensure it's switched out when that bit of code is executed. Quote Link to comment Share on other sites More sharing options...
flashjazzcat Posted December 31, 2008 Author Share Posted December 31, 2008 Also note it's doing INC/DEC of PORTB. That will toggle whether the OS is in or out, so you have to ensure it's switched out when that bit of code is executed. That was one to watch out for, since an interrupt is bound to occur during a CIOV operation, and my patched CIOV routine switches in the OS-ROM before doing a JSR to $E456. To make the patched interrupt service routines (and the patched CIOV calls) as flexible (and reliable) as possible, I always OR PORTB with 1 to re-enable the OS, regardless of whether the OS was switched in beforehand. The code I posted above - with this modification - works perfectly now. 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.