Jump to content
IGNORED

Code under OS ROM


flashjazzcat

Recommended Posts

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...

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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...

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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... ;)

Link to comment
Share on other sites

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...

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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).

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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 by flashjazzcat
Link to comment
Share on other sites

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! :ponder:

Link to comment
Share on other sites

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. :)

Link to comment
Share on other sites

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...