Jump to content
IGNORED

Memory Banking in Hybrid Basic & Assembler


Recommended Posts

Hi All,

 

As an exercise for myself, I am attempting to write a checkbook balancing program that utilizes the extended memory in a 130xe. I am trying to use the four extended banks for data storage of transaction records, leaving the main bank for program use.

 

I am using an assembler subroutine to perform the bank switching and data storage/retrieval with the last function being a return to standard memory map before returning to the calling Basic program. My thinking was that Basic could not handle losing any program lines that fall in the bank window, so I pass control to the machine language subroutine and then restore the main bank before Basic needs to do any further processing.

 

Everything works well so long as my Basic program does not encroach on the bank window, but once the program takes up residence past $4000 I am having a problem with the computer becoming locked up. This lock up does not happen during program execution. It happens if I exit the program (either by Break or quiting the program normally) and then try to set PORTB to any value. I am still in the early stages of program development and want to verify that the data I think is being stored in a given bank is really getting there in the layout I have set up.

 

Am I truly hitting a system limitation here, or is it more likely that the logic of my subroutine is somehow flawed and I am confusing the OS to the point that it just gets lost?

 

I can post both the Turbo Basic code and the source for the machine language subroutine if that will help.

 

Thanks,

 

Russ

Link to comment
Share on other sites

Immediate Basic mode is like running from line 32768. So the problem is anything you type will be tokenized at the end of the program, and any variables will be after that.

 

If the program ended at $8010 then you'd probably have fewer problems. But another thing is line searching, Atari Basic always searches from the start of program, and the links will be broken if the wrong bank is in place and it needs to skip through part of the program that falls in the switchable area.

 

If you could get away with using strings and integers to access the extended banks then it'd be easy to transfer data via a USR routine.

 

I don't think Turbo Basic has ability to return address of a numeric array otherwise you might be able to move stuff in/out of those too.

 

Another alternative could be to use ext Ram as a Ramdisk and use I/O statements to transfer data.

 

Or if you want to stick to the way you're doing things, another alternative - fill your program with REM statements to bloat the program out so it ends after $8000. As you expand the program, remove these lines to fit. At least then you should find immediate mode works to an extent.

  • Like 1
Link to comment
Share on other sites

Turbo Basic itself makes heavy use of PORTB because it utilizes the memory below the OS an assumes it has total over it.

In particular Turbo Basic has the NMI/IRQ handlers in the lower RAM area and the sitches PORTB as required.

It mostly uses INC PORTB and DEC PORTB to switch BIT0.

I assume what happens in you case is that NMI/IRQs kick in while your assembler routine is running.

If they kick in while PORTB is in a state Turbo Basic does not expect, the stack/state will break.

 

In normal Atari BASIC this problem does not exists, because it doesn't change PORTB.

 

So you should make sure your code looks like:

 

sei ;Prevent IRQ
lda #0
sta $d40e ; Prevent NMI
lda $d301
pha

...
pla ;Restore PORTB
sta $d301
lda #$40 ;Re-enable VBI
sta $d40e
cli ;Re-enable IRQ
rts

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

Here's a ML subroutine that gives you access to the extended RAM in a 130XE. It was written for regular Atari BASIC and works with Turbo BASIC as well.

 

It was taken from Compute magazine. Thanks goes to AA user ThumpNugget for the original scans.

 

30200 PROC InitAccessBanked

30240	FOR PageSixMem = 1536 TO 1780
30260		READ MachineCodeByte
30270		POKE PageSixMem, MachineCodeByte
30280	Next PageSixMem

30300	DATA 160,0,173,1,211,41,195,133,217,165,216,41,192,74,74,74,74,9,32,5,217,141,1,211,165,216,41,63,9,64,133,215
30310	DATA 165,212,145,214,165,217,9,48,141,1,211,96,160,0,173,1,211,41,195,133,217,165,216,41,192,74,74,74,74,9,32
30320	DATA 5,217,141,1,211,165,216,41,63,9,64,133,215,177,214,133,212,165,217,9,48,141,1,211,96,104,133,245,198,245
30330	DATA 104,133,216,104,133,214,165,245,208,6,32,44,6,132,213,96,104,104,133,212,32,0,6,24,144,243,104,104,133,225
30340	DATA 104,133,224,104,133,216,104,133,214,104,133,227,104,133,226,160,0,132,229,132,228,177,224,133,212,32,0,6
30350	DATA 230,224,208,2,230,225,230,214,208,2,230,216,230,228,208,2,230,229,165,228,197,226,208,225,165,229,197,227
30360	DATA 208,219,96,104,104,133,216,104,133,214,104,133,225,104,133,224,104,133,227,104,133,226,160,0,132,229,132
30370	DATA 228,32,44,6,165,212,145,224,230,224,208,2,230,225,230,214,208,2,230,216,230,228,208,2,230,229,165,228,197
30380	DATA 226,208,225,165,229,197,227,208,219,96

30390 ENDPROC

 

post-6369-0-40771100-1339777433_thumb.png post-6369-0-20040400-1339777467_thumb.png

  • Like 4
Link to comment
Share on other sites

Yes, exactly what I mentioned in missing. It assumes interrupts don't mess with PORTB.

Do you need it as DATA statements?

If you use TurboBasic anyway I could provide an executable which can be loaded via BLOAD.

 

0600: A0 00            LDY #$00
0602: AD 01 D3         LDA PORTB
0605: 29 C3            AND #$C3
0607: 85 D9            STA $D9
0609: A5 D8            LDA $D8
060B: 29 C0            AND #$C0
060D: 4A               LSR
060E: 4A               LSR
060F: 4A               LSR
0610: 4A               LSR
0611: 09 20            ORA #$20
0613: 05 D9            ORA $D9
0615: 8D 01 D3         STA PORTB
0618: A5 D8            LDA $D8
061A: 29 3F            AND #$3F
061C: 09 40            ORA #$40
061E: 85 D7            STA $D7
0620: A5 D4            LDA FR0
0622: 91 D6            STA ($D6),Y
0624: A5 D9            LDA $D9
0626: 09 30            ORA #$30
0628: 8D 01 D3         STA PORTB
062B: 60               RTS
062C: A0 00            LDY #$00
062E: AD 01 D3         LDA PORTB
0631: 29 C3            AND #$C3
0633: 85 D9            STA $D9
0635: A5 D8            LDA $D8
0637: 29 C0            AND #$C0
0639: 4A               LSR
063A: 4A               LSR
063B: 4A               LSR
063C: 4A               LSR
063D: 09 20            ORA #$20
063F: 05 D9            ORA $D9
0641: 8D 01 D3         STA PORTB
0644: A5 D8            LDA $D8
0646: 29 3F            AND #$3F
0648: 09 40            ORA #$40
064A: 85 D7            STA $D7
064C: B1 D6            LDA ($D6),Y
064E: 85 D4            STA FR0
0650: A5 D9            LDA $D9
0652: 09 30            ORA #$30
0654: 8D 01 D3         STA PORTB
0657: 60               RTS
0658: 68               PLA
0659: 85 F5            STA $F5
065B: C6 F5            DEC $F5
065D: 68               PLA
065E: 85 D8            STA $D8
0660: 68               PLA
0661: 85 D6            STA $D6
0663: A5 F5            LDA $F5
0665: D0 06            BNE $066D
0667: 20 2C 06         JSR $062C
066A: 84 D5            STY $D5
066C: 60               RTS
066D: 68               PLA
066E: 68               PLA
066F: 85 D4            STA FR0
0671: 20 00 06         JSR $0600
0674: 18               CLC
0675: 90 F3            BCC $066A
0677: 68               PLA
0678: 68               PLA
0679: 85 E1            STA $E1
067B: 68               PLA
067C: 85 E0            STA FR1
067E: 68               PLA
067F: 85 D8            STA $D8
0681: 68               PLA
0682: 85 D6            STA $D6
0684: 68               PLA
0685: 85 E3            STA $E3
0687: 68               PLA
0688: 85 E2            STA $E2
068A: A0 00            LDY #$00
068C: 84 E5            STY $E5
068E: 84 E4            STY $E4
0690: B1 E0            LDA (FR1),Y
0692: 85 D4            STA FR0
0694: 20 00 06         JSR $0600
0697: E6 E0            INC FR1
0699: D0 02            BNE $069D
069B: E6 E1            INC $E1
069D: E6 D6            INC $D6
069F: D0 02            BNE $06A3
06A1: E6 D8            INC $D8
06A3: E6 E4            INC $E4
06A5: D0 02            BNE $06A9
06A7: E6 E5            INC $E5
06A9: A5 E4            LDA $E4
06AB: C5 E2            CMP $E2
06AD: D0 E1            BNE $0690
06AF: A5 E5            LDA $E5
06B1: C5 E3            CMP $E3
06B3: D0 DB            BNE $0690
06B5: 60               RTS
06B6: 68               PLA
06B7: 68               PLA
06B8: 85 D8            STA $D8
06BA: 68               PLA
06BB: 85 D6            STA $D6
06BD: 68               PLA
06BE: 85 E1            STA $E1
06C0: 68               PLA
06C1: 85 E0            STA FR1
06C3: 68               PLA
06C4: 85 E3            STA $E3
06C6: 68               PLA
06C7: 85 E2            STA $E2
06C9: A0 00            LDY #$00
06CB: 84 E5            STY $E5
06CD: 84 E4            STY $E4
06CF: 20 2C 06         JSR $062C
06D2: A5 D4            LDA FR0
06D4: 91 E0            STA (FR1),Y
06D6: E6 E0            INC FR1
06D8: D0 02            BNE $06DC
06DA: E6 E1            INC $E1
06DC: E6 D6            INC $D6
06DE: D0 02            BNE $06E2
06E0: E6 D8            INC $D8
06E2: E6 E4            INC $E4
06E4: D0 02            BNE $06E8
06E6: E6 E5            INC $E5
06E8: A5 E4            LDA $E4
06EA: C5 E2            CMP $E2
06EC: D0 E1            BNE $06CF
06EE: A5 E5            LDA $E5
06F0: C5 E3            CMP $E3
06F2: D0 DB            BNE $06CF
06F4: 60               RTS

Link to comment
Share on other sites

Rybags,

 

If I am understanding your reply, it would seem that Atari Basic is in fact easily confused when the banks get switched. Is that still true when the banks are manipulated during a USR call? I am not trying to do any PORTB changes in the BASIC part of the program.

 

In fact, the last thing the USR routine does is set PORTB back to its default value of 254.

I am also using Turbo Basic which is why the PORTB setting includes the value for BASIC off.

 

JAC!,

 

Thanks for telling me about Turbo Basic's internal handling of PORTB. I will try encapsulating my ML routine in the code you described and see where that gets me. I must admit that I am getting lost in the second bit of code you listed. Does it have something to do with MrFish's post? If so, I have not yet digested that piece. Hopefully, your code will make more sense to a novice like myself once I have.

 

MrFish,

 

I have not yet had a chance to fully go over your suggestion, as I mentioned to JAC!. I will certainly go over your input and put it to good use. Thanks.

 

All,

 

Thanks for all the input. I really appreciate your willingness to help as this bump in the road has really got me stumped. I should be trying some of your suggestions out this weekend and I should be posting the results in this thread by Sunday evening (USA West Coast Time, that is).

 

Russ

Link to comment
Share on other sites

254 = OS off, Basic Rom on. Unsure if that's what you want.

 

Best practice is to not assume anything, get PORTB state and store it somewhere or push to the stack.

 

A USR routine should be free to do what it wants, but if T-Basic does have IRQ/NMI routines in place then you've got that potential point of failure there.

 

Assuming you don't need the OS or Basic state to be one way or the other, you could use AND/OR to control your bank selecting rather than using specific values.

Chances are you could leave IRQs and NMIs enabled as well.

Link to comment
Share on other sites

Rybags,

 

Strange. I don't have access to my 130xe at the moment and I have been using Altirra for program development. When I use Turbo Basic, I get a value 254 from Peek($D301). Atari Basic does give a value of 253 from a similar Peek. Sounds like a flaw in emulation or else Turbo Basic would have to contain a custom OS to take the place of the installed one.

 

Russ

Link to comment
Share on other sites

IIRC Turbo Basic uses Ram under both OS and Basic, that's the reason it's incompatible with many modern Dos setups.

 

If you do a USR routine that returns PORTB you get $FF - T-Basic swaps the OS in/out as required depending on what statement is running.

 

Checking the Ram-based NMI handler - the DLI handler leaves PortB alone so any DLI would have to assume an indeterminate state. Most DLIs don't need the OS Rom present anyway. Any user supplied DLI would need to preserve the state of PORTB.

The VBlank handler pushes a return address to the stack so it can get control back, switches PortB to $FF via an INC, then branches thru VVBI ($222). Once control is returned it restores PortB via DEC and returns from the NMI.

 

The Ram-based IRQ handler does the same thing.

 

So, the way things are set up, the state of PortB can change dynamically and the Interrupt handlers will cope with it.

 

The things a user program need to do - when changing PortB, preserve and restore it's state, and don't assume the OS/Basic Rom state will be predictable.

Link to comment
Share on other sites

I've put it back into source, optimized the size so it still fits into page 6 and put in the NMI/IRQ handling.

BLOAD "D:ACCESS.XEX" will load it to page 6.

New addresses are:

auxbyte=$0667

auxdump=$069E

auxload=$06C0

 

Done blindly - I don't have a program to test. So in case it does not work, please post the surounding code also.

Access.xex

Edited by JAC!
Link to comment
Share on other sites

JAC!,

 

I must have a fundamental misunderstanding of how to apply the wrapper code from your first post. I have attached my subroutine, in text format, to this post. Maybe you can tell where my fatal flaw is located.

 

Here is what is happening. When I run my Turbo Basic program (I am BLOADing my subroutine in as a part of the initialization process), I can execute the proc to enter data for a check record. Upon completion of data entry, the proc moves the record to a reserved buffer in hi memory and calls my subroutine. I get machine lockup at this point.

 

Let me know if you need to see the Turbo Basic code. I can upload it here, if needed.

TSTTBRS.TXT

Link to comment
Share on other sites

Hmm, looks like X is intend to hold the bank index, but then it makes no sense to use it only when it's 3 (or 2 for the other case).

0100     PLA         ;DISCARD HI BYTE OF FLAG. IT WILL NEVER BE >255
0110     PLA         ;GET VALUE FOR FLAG
0120     TAX         ;SAVE FLAG IN X REGISTER
0130     PLA 
0140     STA INDEX+1 ;HI BYTE OF INDEX TO NEXT RECORD
0150     PLA 
0160     STA INDEX   ;LO BYTE OF INDEX TO NEXT RECORD
0161     LDA $D301
0162     PHA 
0170     CPX #3      ;IS FLAG AN ATM/POS RECORD?
0180     BEQ GOATM   ;YES, THEN GO ATM SUBROUTINE
...
0230 GOATM JSR SVPRTB ;RUN ATM/POS SUBROUTINE
0231     JSR ATMSUB
..
0270 ATMSUB LDA BANKCD,X ;GET BANK # FOR ATM
0280     STA $D301   ;SET BANK FOR ATM RECORDS

Link to comment
Share on other sites

JAC!,

 

X gets used in 3 cases, when it is 0 & 1 then the record is for a check. Banks 0 & 1 are used to store check records. When X is 2, the record is a deposit and Bank 2 is used. When X is 3, the record is for an ATM/POS record and Bank 3 is used.

 

I have only tested the check record piece so far and only the first bank of checks. The value placed into X comes from the Basic program as a part of the USR call.

 

Do I have a logic error in the way I have tried to implement your suggested code?

Link to comment
Share on other sites

Yes, this PLA will pull a byte from the return address which was pushed by JSR.

 

RESET PLA ;GET SAVED PORTB STATE

0570 STA $D301 ;RESTORE PORTB STATE

0580 LDA #$40 ;RESTORE NMI

0590 STA $D40E

0600 CLI ;ENABLE IRQ

0610 RTS

 

 

Link to comment
Share on other sites

Maybe it'd be easier to write a generic routine to get/put short segments up to 256 bytes.

 

USR allows an integer up to 65535, so you could use that to determine bank and address.

 

Shift high byte left 4 times, AND #$0C gives you the bank number. And high byte with #$3F then OR #$40 gives the absolute address.

Link to comment
Share on other sites

JAC!,

 

I failed to save the state of $D301 in the source code I uploaded earlier. Here is a corrected version. This code seems to execute, but returns to a Basic READY prompt with a lock-up. Progress, but not exactly the intended result.

 

I think I am doing the following:

 

Basic places the length of the record, a bank flag, and the offset into the bank on the stack.

The subroutine pulls each of these elements from the stack, leaving just the return address for the USR call.

The routine calls the code to stop maskable interrupts, Non-maskable interrupts, and then places the current state of $D301 onto the stack.

The requested record type is stored in extended memory

The routine calls the code to restore the state of $D301, returns execution of Non-maskable interrupts to service, returns maskable interrupts to service and then returns to Basic

 

Some part of this is not true. I am just not getting the part that is broken. Can you make any suggestions for what to look at?

 

Rybags,

 

Sir, your assembler accumen is beyond my measley understanding. I am having trouble grasping how the high byte of an integer (multiplied by 16 and having its bit2 & bit3 masked off) can give the selected bank.

 

Don't get me wrong. I believe you when you say this works. I just can't make that leap in my own head. Can you explain the workings? Thanks.

TSTTBRS.TXT

Link to comment
Share on other sites

All,

 

I have confirmed that any time my program invades addresses above $4000, I cannot issue a POKE command to $D301 without a lockup being the result. I have determined a somewhat clumsy work around for testing purposes.

 

Load and run the program implementing an ML subroutine containing the wrapper code JAC! recommended. When the test run is complete, quit the program. Type NEW to remove any program lines that might be living in the access window. Issue the immediate mode command to set PORTB to the required value. Validate that the expected data is there. Cold start, reload the program and continue testing. A bit more cumbersome than I would like, but it works.

 

I want to thank both MrFish and JAC! for supplying a canned solution that I may still choose to implement, but I think I will continue to test with my own code for now. It is helping me to become familiar with 6502 assembler in ways that a pre-programmed solution would not.

Link to comment
Share on other sites

I've put it back into source, optimized the size so it still fits into page 6 and put in the NMI/IRQ handling.

BLOAD "D:ACCESS.XEX" will load it to page 6.

New addresses are:

auxbyte=$0667

auxdump=$069E

auxload=$06C0

 

Done blindly - I don't have a program to test. So in case it does not work, please post the surounding code also.

 

This is interesting information. I've been using the routine that I posted above with Turbo-BASIC since the late 80's. I've never experienced a problem with the routine or Turbo-BASIC itself as a result. As a matter of fact I'm currently using this routine in my font converter for the GUI, with which I've converted hundreds of megabytes of font data.

 

Is it possible to give a more concrete example of how the problem would manifest itself as a result of using the routine that I've posted?

Link to comment
Share on other sites

MrFish,

 

Was this last post directed at me or JAC!? I have not implemented ACCESS in my program, yet. I have been testing with my home-grown routine wrapped in JAC!'s code to toggle IRQ & NMI at appropriate places.

 

I need to compare how ACCESS uses PORTB with my routine. I might find something enlightening. Hopefully, I will get to this before week's end. I will keep you posted.

Link to comment
Share on other sites

MrFish,

 

Was this last post directed at me or JAC!? I have not implemented ACCESS in my program, yet. I have been testing with my home-grown routine wrapped in JAC!'s code to toggle IRQ & NMI at appropriate places.

 

I need to compare how ACCESS uses PORTB with my routine. I might find something enlightening. Hopefully, I will get to this before week's end. I will keep you posted.

 

It was directed more at JAC, or Rybags. I know you're trying to improve your assembler experience Kylev. I just posted the routine for reference, or for anybody who's never seen it before.

Link to comment
Share on other sites

  • 3 months later...

I was looking for information on 130XE bankswitching, and came across this thread ... this routine could serve my needs, but I have a few problems.

 

The situation is, I am experimenting with a Puzzle Bobble type game ... it uses a software graphics mode, so I'm basically using a VBI, a character set DLI, and two stored display lists.

 

My memory map for this is:

 

VBI: 1536 (start of page 6)

Display list 1: 1576

Display list 2: 1654 (31 bytes)

DLI: In a string

Table for VBI: 1024 (start of page 4)

Table for DLI: 1058

Character sets: 16 pages below RAMTOP

 

I also want to bring PMG's into this as well, and the idea is to store character sets in extended ram and move them into my char set memory as they are needed. The game will have 15 sets of level graphics, which will all fit into the extended memory of the 130XE.

 

The problem is, the routine (as shown in the compute magazine article) resides in the start of page 6, but I am using that area for my VBI and display lists. Ideally I want the routine to reside starting at 1686 (past the end of my second display list) ... how do I modify this routine to achieve this?

 

I am including the .ATR here with the accompanying program PB1.TUR if anyone here wants to have a look and see what I've done. Lines of note:

 

600-620: Reserve 16 pages below RAMTOP and load fonts

630-640: POKE in Display List 1 (Antic 2 - Graphics 10)

650-850: POKE in Display List 2 (Antic 4 - Graphics 12 with horizontal scrolling)

900-950: Initialize VBI routine

951-954: Initialize DLI routine (the routine is at line 20000)

960: Display screen

puzzle bobble level 1 ice font test.atr

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