Jump to content
IGNORED

Developing a new language - ACUSOL


Pab

Recommended Posts

I don't understand your bankswitching code, it's a mess. I think your first mistake is saving $D301 from within the DOS call. Under DOS 2.0 or MyDos this should not be an issue, but I can see where it might cause trouble under SpartaDos. I think you need to have a loader stub that saves $D301 itself and then loads the banks itself since I don't recall DOS 2.0 having the XIO #40 to do a binary load. I would swap banks before invoking any DOS function.

 

I also don't see where you interrogate the hardware to determine just what banks are available. Unless you've set a minimum standard, I would think that would have to be the first order of business, determining what hardware is available.

Link to comment
Share on other sites

How does this sound for an option? Segments that need to be loaded into banked RAM will actually be loaded into memory starting at $8000 and moved after the segment is loaded. This would mean that any code destined to be compiled into the area from $8000-$BFFF would have to come in a later MODULE than the routines destined for the banks. Or, if you plan on having the main program in cartridge RAM at $A000-$BFFF (including the runtime), you just have to make sure you're not putting more than 8K in any MODULE. Sound like a reasonable restriction?

Yes, but remember you can make the transient buffer smaller if need be and make the segments correspondingly smaller too (for example, copying four contiguous 4KB code blocks into an extended bank). This might relieve some knock-on restrictions.

Link to comment
Share on other sites

Right now I don't interrogate the hardware. At this stage I'm still only playing with the 4 "XE" banks, which are pretty standard.

 

In the language's logic, bank 0 is the main RAM bank. Any address referenced without a bank number or as being in bank 0 is assumed to be in main RAM. Instead of higher banks being addressed through the bits switched in PORTB, however, I apply a bit of logic to calculate which bits to turn on based on a bank number from 1-32.

 

First, I query what's in PORTB at the moment and do an AND #01 so I don't go swapping out OSRAM if it's active. Then I calculate which bank we are swapping in.

 

Banks 1-4 are the XE banks, set through bits 2 and 3 of PORTB. (The language subtracts 1 from the bank number specified before calling routines, so "Bank 1" becomes XE Bank 0.

0160  LDA BANK
0170  AND #$03 ; Check the 4 lowest banks
0171  ASL A
0172  ASL A
0180  STA SCRATCH

Banks 5-8 (internally 4-7) are presumed (at this moment) to be part of a 320XE upgrade, addressed through bit 5 of PORTB.

0190  LDA BANK
0200  AND #$04 ; Check the bit for 256K upgrades
0210  ASL A
0211  ASL A
0212  ASL A
0220  ORA SCRATCH
0230  STA SCRATCH

Banks 9 through 16 (internally 8-15) are treated as a 576K upgrade addressed through bit 1 of PORTB

0240  LDA BANK
0250  AND #$08 ; Check for 576K upgrade banks
0260  LSR A
0261  LSR A
0270  ORA SCRATCH ; In the BASIC bit
0280  STA SCRATCH

And if we have a 1Mb upgrade, banks 17-32 are assumed to be addressed through bit 6 of PORTB.

0290  LDA BANK
0300  AND #$10 ; A megabyte upgrade?
0301  ASL A
0302  ASL A
0303  ORA SCRATCH
0305  STA SCRATCH ; We have set all the bits

This value is then OR'd with the old value of PORTB and stored.

 

So, if you have a 1Mb upgrade and want to access the highest bank of RAM (Bank 32), the bank selection routine would turn on bits 2, 3, 5, 1, and 6 of PORTB. If you tried accessing bank 31, it would turn on 3, 5, 1, and 6.

 

I know there has to be a better way, but this is what I have for now. It's easy to pull that code out and replace it however.

 

The first segment of the file is a quickie stashed in the cassette buffer which copies the current value of PORTB into $CA. I did that only so I have a value to use when I want to force my way back into the main bank after loading data into a bank.

Link to comment
Share on other sites

Yep: I think I already posted a link to a good generic RAM detection routine you might use. But the method for getting code into extended memory is the same regardless of how many banks are present. Bank 0 for main bank makes perfect sense, as does caching the original PORTB value.

Link to comment
Share on other sites

My thinking is that most developers are going to want to develop for the lowest common denominator, so there isn't really a need for detection at the language level. They aren't going to stash variables or procedures anywhere other than the four banks they can be assured every machine with extended memory has.

 

The place for detection is at the RTL level, so at runtime a program can decide where it wants to or can stash data. So if it wants to put buffers, tables, etc. in extended RAM it can make calls to routines like "CheckAvailableBanks" or "IsBankAvailable" or some such. For example, the bootstrap compiler uses a 16k code buffer to minimize writing, which would be a natural for an empty bank in the native Atari version.

 

Those RTL routines could also detect if the program is running under SpartaDOS X so it would only use banks left free at the DOS level.

Link to comment
Share on other sites

My thinking is that most developers are going to want to develop for the lowest common denominator, so there isn't really a need for detection at the language level. They aren't going to stash variables or procedures anywhere other than the four banks they can be assured every machine with extended memory has.

 

The place for detection is at the RTL level, so at runtime a program can decide where it wants to or can stash data. So if it wants to put buffers, tables, etc. in extended RAM it can make calls to routines like "CheckAvailableBanks" or "IsBankAvailable" or some such. For example, the bootstrap compiler uses a 16k code buffer to minimize writing, which would be a natural for an empty bank in the native Atari version.

 

Those RTL routines could also detect if the program is running under SpartaDOS X so it would only use banks left free at the DOS level.

Well quite: the idea was that the detection routine runs at application run time and thus the BANK values the user hard-coded into the source become mapped to an array of arbitrary PORTB values which the coder can't know in advance and doesn't have to worry about (at least as far as code blocks are concerned).

Edited by flashjazzcat
Link to comment
Share on other sites

Alfred: originally it was supposed to load a procedure into each of the four standard XE banks which called a PrintE routine, then the main procedure would call each of the four procedures in turn in each bank. Trying to use DOS to load directly into each bank proved to be a mistake as was pointed out by flashjazzcat upthread, so I've abandoned that approach for now in favor of a load-move routine.

 

flashjazzcat: I've decided to go with your approach and build a bank table. Will just need to decide on a safe place to stash it.

Link to comment
Share on other sites

Just realized one of my other problems with SpartaDOS. Along the way I somehow ended up with two DEVICE RAMDISK statements in my CONFIG,SYS, so I had the main RAMDISK installed, and then another one in the XE banks that I had assumed were available. That was my own stupid fault.

Link to comment
Share on other sites

Well you're free to implement what you like, but I disagree that direct bank loading via DOS is a mistake. I've used it myself in the past, so I know it works. Load and copy is going to be pretty slow.

 

Under Sparta?

 

Well, I'm rewriting my banking code from the ground up, so I'll try it again with the new code. I agree that direct loading is much preferable to load-and-move.

Link to comment
Share on other sites

Yes, under SpartaDos, although 3.2 and not SDX. My approach wasn't exactly like yours however. A kernel loaded first and then it switched to each bank in succession and loaded a program into each bank of memory. Your approach is sound however, and if it doesn't work either there is a bug in the PrintE bank code, or (my guess) is that there is a bug in the bank switching code; either a bad mask or an invalid bank # is being used.

Link to comment
Share on other sites

The PrintE is the same as I've been using and is sound. I posted the object code here a while back. It has to be in the bank code. Like I said, I'm tearing it down and rebuilding it from the ground up, so we shall see.

 

Banks outside of the original 4 were never my specialty. I didn't have an expanded system until late in the game and never wrote a program that used anything other than the original 4, leaving the rest for ramdisks.

Link to comment
Share on other sites

Well you're free to implement what you like, but I disagree that direct bank loading via DOS is a mistake. I've used it myself in the past, so I know it works. Load and copy is going to be pretty slow.

I just don't think it's safe and reliable, and therefore it's inadvisable. I'd have to use the Altirra debugger to verify this, but I suspect that (for example) when DOS 2.5 grabs a sector from the RAMdisk (requiring writes to PORTB), it won't necessarily restore the banking register's original state (it may simply be hard-coded to restore the base bank). Whether this is the case, I'm not completely certain, but I can't say it isn't the case, and unless it's written somewhere that direct binary loads into extended RAM are guaranteed to work with all widely used DOS packages, then my advice would be: don't assume it will work.

 

As for load and copy being slow: I don't get that at all. Apparently we're talking about a supported maximum of four extended banks here, amounting to no more than 64KB of code (which is obviously an extreme case: I know of few people churning out executables of half that size). My word processor The Last Word loads 14KB of itself under the OS, which is necessarily a (two stage) load and copy operation, and frankly you can't perceive it happening. The 6502 can (I guess) shift around 40-50KB/s, so I would not regard load and copy as a significant overhead.

 

Banks outside of the original 4 were never my specialty. I didn't have an expanded system until late in the game and never wrote a program that used anything other than the original 4, leaving the rest for ramdisks.

This was my experience when I developed all my applications on a 130XE back in the 90s. When I came back to the Atari in 2008 and released some stuff, one of the first things I was asked to do was support more than the measly 128KB of yesteryear. Even 320KB seemed like unimaginable luxury back in the day: something I just read about in magazine ads. First thing I noticed in 2008 is that lots of people had 1MB Ataris. It took a few days of research to support these upgrades, but once it's done, it's done, and you have your re-usable code library. Or - to reiterate - you can use something pre-written which works:

 

Edited by flashjazzcat
  • Like 1
Link to comment
Share on other sites

GAH! Shows how stupid I can be some times. I adapted flashjazzcat's routine for identifying and counting available banks, but I'd only allocated 64 bytes for the bank table. Then when it identified 65 banks (I had forgotten about $FF for the main bank) it overwrote the first byte of my routine to actually switch banks.

 

Writing self-modifying code is cool. Writing accidentally self-modifying code is not.

  • Like 1
Link to comment
Share on other sites

I did something very similar the other day when I added an extra display line to a text editor and forgot to enlarge the line length table. Anyway, I wouldn't worry about 2MB upgrades right now, although a generic detection routine should theoretically find as many banks as are accessible through PORTB. You can just stop looking after you find 64 banks.

 

If you need to know how to read the SDX banking tables (which you should do if you detect SpartaDOS X) and calculate free banks, banking masks, etc, let me know, although IIRC there are also some code samples on the same site as the RAM detection routine.

Link to comment
Share on other sites

Tested under SDX 4.46, SD 3.2f, BW DOS, MyDOS 4.53, and even DOS 2.5. (I have discovered through this process that Altirra does not like DOS 3. That's how thorough I wanted to be.) Under SDX, all routines that use banked RAM are disabled. (USE OSRAM/DEVICE SPARTA OSRAM, no RAMDISK, DOSKEY, etc.) Under DOS 2.5, RAMDISK.COM renamed to RAMDISK.SYS to keep it from running.

 

Observable behavior:

  1. First segment, the "return to main RAM" routine, loads.
  2. Second segment, the rest of the banking runtime. Loads 65 bytes after the end of "return to main RAM" to leave room for the bank table.
  3. Third segment loads and runs at $480. This is the adaptation of flashjazzcat's code to detect banks and builds a bank table at $1F08 (right after "return from bank"),
  4. Next, the "Hello from bank 1" routine loads at $8000.
  5. A relocator runs at $480, switching in bank #1 and moving the code. Monitored in Altirra's debug mode, seems to work fine.
  6. "Hello from bank 2" loads at $8000
  7. Relocator swaps in bank 2 and seems to move everything okey-dokey.
  8. "Hello from bank 3" loads
  9. Relocator seems to successfully swap it into bank #3.
  10. "Hello from bank 4" loads.
  11. Relocator seems to successfully swap it into bank #4.
  12. "Main" segment loads
  13. RUNAD starts program at $21CC
  14. "0" (for bank 1) stored into $CF
  15. Bank selection routine JSR'd at $1F49.
  16. Appropriate value loaded into PORTB. Selected bank swaps in.
  17. Bank selection routine RTS's.
  18. Program JSR's to $4000, where the "Hello from bank 1" procedure lives.
  19. System freezes.

Same situation appears under each DOS.

 

I am at a loss.

test16.bin

Link to comment
Share on other sites

I did something very similar the other day when I added an extra display line to a text editor and forgot to enlarge the line length table. Anyway, I wouldn't worry about 2MB upgrades right now, although a generic detection routine should theoretically find as many banks as are accessible through PORTB. You can just stop looking after you find 64 banks.

 

If you need to know how to read the SDX banking tables (which you should do if you detect SpartaDOS X) and calculate free banks, banking masks, etc, let me know, although IIRC there are also some code samples on the same site as the RAM detection routine.

 

I will certainly get to you down the road for that. As for the site with the RAM detection routine, I can't read Polish and Babelfish only goes so far. :)

Link to comment
Share on other sites

The program crashes because you JSR to bank switching code at $1F00 from inside the banking window (at $4023 to be precise). The subroutine's return address immediately becomes invalid (in fact you RTS into a bunch of zeros).

 

Note I did not write the RAM detection routine. Regarding wiki content: I used Google Translate or similar, although I did have prior tuition on the subject of SDX bank handling from Drac030. ;)

 

Now: I'm really curious to know why you think Altirra won't run DOS 3???

Edited by flashjazzcat
  • Like 1
Link to comment
Share on other sites

            lda #$40               ; A9 40
            sta L00A1              ; 85 A1
            lda #$02               ; A9 02
            sta L00A2              ; 85 A2
            jsr L1F00              ; 20 00 1F
            jsr L215E              ; 20 5E 21
            pha                    ; 48
            lda #$01               ; A9 01
            sta L00CF              ; 85 CF
            jsr L1F49              ; 20 49 1F
            pla                    ; 68
            rts                    ; 60

How the hell did that get in there? Everything between the STA $A1 and the RTS should not have been there, except for the JSR $215E. Going to be a long night stepping through Pascal source, it looks like.

Edited by Pab
Link to comment
Share on other sites

Not that it does anyone any good, but for the curious this is the source that I'm trying to compile right now. For the curious and the strong of stomach.

MODULE ORG=$1F00

USES PRINT

MODULE ORG=$4000:1

PROC Bank1
   PrintE("Hello from bank #1!")
RETURN

MODULE ORG=$4000:2

PROC Bank2
   PrintE("Hello from bank #2!")
RETURN

MODULE ORG=$4000:3

PROC Bank3
   PrintE("Hello from bank #3!")
RETURN

MODULE ORG=$4000:4

PROC Bank4
   PrintE("Hello from bank #4!")
RETURN

MODULE // Return to where we left off in main bank.

PROC MAIN
   Bank1
   Bank2
   Bank3
   Bank4
RETURN
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...