Jump to content
IGNORED

Developing a new language - ACUSOL


Pab

Recommended Posts

Oh, for the....

 

I have to stop programming late at night. This was the line causing all the trouble...

   If p.VBank <> ProgramBank Then JSRB(p.VAddress,p.VBank) Else JSR(p.VAddress);

In other words, if the procedure is in a bank other than the one you are in, switch to that bank and JSR. Unfortunately, not only did I not check whether the procedure being called is in shared memory (i.e., no bank), but it never occurred to me that a routine in one bank would be calling another bank!

 

Nothing is ever foolproof because we fools are so ingenious.

  • Like 1
Link to comment
Share on other sites

Writing bank switching code is a lot of fun [irony]. ;) There's nothing stopping code in one bank calling a routine in another bank (providing the bank-switching routine is situated outside of the banking area), as long as the bank switching routine ensures that RTS in the target routine causes the originating bank to seamlessly switch back in before the instruction following the original JSR is hit.

This is the bankswitching code I came up with for the Ultimate/Incognito PBI BIOS, if it's of any interest:

; Must be outside of banking area


longjsr ; call with address of routine-1 in x,a and bank # in y (note only the processor status and Y are preserved on return)
	jsr Exec
	php
	dec BankSp ; stack pointer (initialized to zero)
	ldx BankSp
	lda BankStack,x ; bank # stack
	sta BANK ; activate bank
	plp
    	rts

Exec
	pha ; save address
	txa
	pha
	sty BANK ; activate bank
	tya
	inc BankSp
	ldx BankSp ; save bank # on stack
	sta BankStack,x
	rts



ljsr	.macro
	ldx #< [:1-1]
	lda #> [:1-1]
	ldy #:2
	jsr longjsr
	.endm

To perform a in inter-bank JSR, you just write "LJSR <ROUTINE> <BANK>". For PORTB banking, you can just use the value here stored in BANK as an index into an array of PORTB values.

Edited by flashjazzcat
Link to comment
Share on other sites

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.

 

That's a pretty cynical point of view, that all programmers are retards who don't know how to code. I generally work on the assumption that the programmer knew what he was about, so in the case of DOS 2.5 that it will correctly save and restore the $D301 mask as required. Now I've generally only worked with SpartaDos, so maybe I've been spoiled by the fact that Gustafson is a fairly clever and organised fellow.

 

That Polish ram check routine looks clever, but I'm not convinced it's correct for all cases. It's only checking that $4000 is ram, and it only checks $0 and $4000 of the main bank. So it will detect that a Rambo XL has two extra banks, main bank $8000 and $C000. It also assumes a specific switching pattern, which might not be true. The only way to be sure is to strobe all 254 combinations of $D301 and check each 16K page.

Link to comment
Share on other sites

That's a pretty cynical point of view, that all programmers are retards who don't know how to code. I generally work on the assumption that the programmer knew what he was about, so in the case of DOS 2.5 that it will correctly save and restore the $D301 mask as required. Now I've generally only worked with SpartaDos, so maybe I've been spoiled by the fact that Gustafson is a fairly clever and organised fellow.

Where did I say all programmers are retards? Maintaining the base bank during a binary load wouldn't be a retarded decision on the part of the DOS developer: in fact it's perfectly reasonable, unless you assert somewhere that DOS supports binary loading directly into extended banks (which it might: I'm happy to be referred to documentation or circumstantial evidence that supports the idea that all DOSes can absolutely be relied upon to support this facility, in which case happy days. Saving and loading of buffers out of banked RAM is certainly mandatory, so maybe it should work). It's obvious that if you assume binary loads into extended RAM are possible but DOS happens to reinstate the base bank, your software will crash. You can take that risk, or implement some simple workarounds to ensure the application is in control of the situation at all times.

 

That Polish ram check routine looks clever, but I'm not convinced it's correct for all cases. It's only checking that $4000 is ram, and it only checks $0 and $4000 of the main bank. So it will detect that a Rambo XL has two extra banks, main bank $8000 and $C000. It also assumes a specific switching pattern, which might not be true. The only way to be sure is to strobe all 254 combinations of $D301 and check each 16K page.

Well, I've used it but I have no special attachment to it and if anyone can suggest or write something better for Pab, I'm sure he'd be happy to use it. :)

Edited by flashjazzcat
Link to comment
Share on other sites

Well, to answer the question once and for all, here's a short program which causes two segments to be loaded directly into consecutive extended banks. The segments are then called in turn when the load is complete.

	icl 'common.inc'
	icl 'macros.inc'

	org $2000
	
Start
	mva BaseBank portb
	jsr printf
	.byte 'This is the main bank',155,0
	
	mva banks portb ; call code in bank 1
	jsr Bank1
	
	mva banks+1 portb ; call code in bank 2
	jsr Bank2
	
	mva BaseBank portb
	
	rts
	
	
	
	
Init1 ; test for extended RAM and enable bank 1
	lda portb
	sta BaseBank
	jsr MapExtRAM
	cpb ExtBanks #2
	bcs @+
	jsr printf
	.byte 'No extended RAM!',155,0
	jmp ($0a)
@
	mva banks portb ; switch in bank #1
	rts
	
	icl 'printf.s'
	
	icl 'bankmap.s'


	org $2E2
	
	.word Init1
	
	org $4000 ; bank #1
	
Bank1
	jsr printf
	.byte 'This is bank 1',155,0
	rts
	
	org $3000
	
Init2
	mva Banks+1 portb ; second bank
	rts
	
	org $2E2
	
	.word Init2
	
	org $4000 ; bank #2

Bank2
	jsr printf
	.byte 'This is bank 2',155,0
	rts
	
	org $2E0
	
	.word Start
	

bankload.xex

 

It works perfectly under SpartaDOS X, which is the only DOS I tested it with. But if it works under every other popular DOS, then it would be reasonable to load binary segments directly into extended RAM.

 

post-21964-0-16242700-1396356831_thumb.png

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

Maintaining the base bank during a binary load wouldn't be a retarded decision on the part of the DOS developer: in fact it's perfectly reasonable

No, it's not reasonable, it's completely unreasonable. Since the ability to change banks exists, stuffing a fixed bank value in would be sloppy programming by a poor coder.Proper technique would be to save the existing mask, change only what you needed to change for the function requested and then put the original mask back. For example, I would also expect no DOS written for the XL/XE series would ever assume the OS rom was switched in, because it may very well not be given the existence of things like the translator disk.

 

That's the other problem with that Polish routine: he keeps masking the OS rom back in, which is an unwarranted assumption.

Link to comment
Share on other sites

No, it's not reasonable, it's completely unreasonable. Since the ability to change banks exists, stuffing a fixed bank value in would be sloppy programming by a poor coder. Proper technique would be to save the existing mask, change only what you needed to change for the function requested and then put the original mask back. For example, I would also expect no DOS written for the XL/XE series would ever assume the OS rom was switched in, because it may very well not be given the existence of things like the translator disk.

Well, look here: I've made assumptions about all kinds of things in the past and run into no end of problems because I credited third-parties with common sense. So perhaps I've become a little cautious as a result. But in any case, I ran the test program above with DOS 2.5 as well, and it worked, so it looks as if I was wrong and it's safe enough to load binary segments directly into extended RAM. But I wouldn't assume without seeing it with my own eyes. :D

 

That's the other problem with that Polish routine: he keeps masking the OS rom back in, which is an unwarranted assumption.

Yeah: I used it in a program which also manipulated BASIC and required a RAM-based OS. It was no problem to mask the required bits when extracting values from the table.

Link to comment
Share on other sites

No, it's not reasonable, it's completely unreasonable. Since the ability to change banks exists, stuffing a fixed bank value in would be sloppy programming by a poor coder.Proper technique would be to save the existing mask, change only what you needed to change for the function requested and then put the original mask back. For example, I would also expect no DOS written for the XL/XE series would ever assume the OS rom was switched in, because it may very well not be given the existence of things like the translator disk.

 

That's the other problem with that Polish routine: he keeps masking the OS rom back in, which is an unwarranted assumption.

I suspect, that you confuse reason with specification.

If the specification says, 'We will change the bank without restoring it', that's perfectly reasonable. Then it's on the programmer of an application to maintain a shadow of the bank register and test, that the correct bank is on.

That's the reason, we have to have APIs.

E.g. when DOS 2.5 is developed under the assumption, that, if the ramdisk is running, no other program is using the banks, then it's perfectly reasonable not to restore the bank, because it's faster.

Edited by JoSch
  • Like 2
Link to comment
Share on other sites

Just waiting for the updated version of Lazarus (they released 1.2, I was using 1.0.8) to install on my computer before I get back to work on this beast.

 

I did get a chance to weed out a lot of redundant code in my variable assign routine. I had multiple checks for conditions (are both variables outside of banked area? Are they in the same bank? Is one banksafe and the other not? etc.) under each of the possibilities for assigning value to a variable (numeric constant, another variable, a function call, an object method). Then it occurred to me that to properly support banking I only needed to write one subroutine that would do all the checking and assign the value accordingly.

 

So I just wrote a subroutine that would check whether the location being written to needs to be banked or not, switch the bank accordingly, store the value, and pop back into the original bank (or, if the code itself is in a banked area, JSR to a runtime routine to do the whole thing). Then I just meticulously went through my old routine removing all the checks and replacing LDA xx/STA yy.

 

I also realized just how much of an assembler I'd actually written into procedures in my Pascal source, to the point that sometimes at first glance it's hard to tell whether you're reading a Pascal program or a 6502 assembler program.

Procedure TCompiler.BankPoke(Addr : Word; Bank : Byte);
Begin
   If (Bank <> ProgramBank) And ((Addr > $3FFF) And (Addr < $8000)) Then Begin
      PHA;
      LDAImm(Lo(Addr));
      STA($CB);
      LDAImm(Hi(Addr));
      STA($CC);
      LDAImm(Bank);
      STA($CD);
      PLA;
      JSR(BankPokeAddress);
   End;
End;       
Link to comment
Share on other sites

I also realized just how much of an assembler I'd actually written into procedures in my Pascal source, to the point that sometimes at first glance it's hard to tell whether you're reading a Pascal program or a 6502 assembler program.

I get a similar feeling looking at the source to bankload.xex. :) MADS assembler source can look quite approachable.

Link to comment
Share on other sites

post-12895-0-64797500-1396379094_thumb.gif

 

Eureka.

 

I got a little creative with this one. Self-modifying code! A routine in the runtime switches banks, JSR's to the routine it's calling, then pops back into the original bank. How does it know where to JSR? The program plugs the address right into the JSR statement. (In this compilation's case, at $1F5A and $1F5B.)

 

Source code compiled:

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! Let me check in on bank 2 again.")
   Bank2
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
   PrintE("Back from bank 1.")
   Bank2
   Bank3
   Bank4
RETURN

The procedures in the banks are loaded directly into banked RAM during binary load, with brief routines at $480 to do the switching through INIT as needed.

 

test16.bin

  • Like 2
Link to comment
Share on other sites

Trying it under MyDOS with different memory sizes....

 

1088K upgrade: works.

576K Compy: Crash in detection routine.

576K: Detects 20 banks.

320 Compy: Crash in detection routine.

320 Rambo: Crash in detection routine.

Stock XE: Works.

 

Crashes are happening when attempting to verify bank $3D. Happening under both Altirra and Atari800Win

Link to comment
Share on other sites

Yes. Here is the original source. With a few modifications, it should look familiar. :)

0000        10          *=   $480      
D40E        20 NMIEN    =    $D40E     
00C9        30 BANKS    =    $C9       
00CA        40 OLDPORTB =    $CA       
D301        50 PORTB    =    $D301     
8000        60 SAVETABLE =   $8000     
0480        65 BANKTABLE =   $480      
            70 START
0480 78     80          SEI            
0481 AD0ED4 90          LDA  NMIEN     ; TURN OFF INTERRUPTS
0484 48     100         PHA            
0485 A900   110         LDA  #0        
0487 8D0ED4 120         STA  NMIEN     
048A AD01D3 130         LDA  PORTB     
048D 85CA   140         STA  OLDPORTB  
048F 48     150         PHA            
0490 A200   170         LDX  #0        
            180 TESTLP1
0492 8E01D3 190         STX  PORTB     
0495 AD0040 200         LDA  $4000     ; SAVE MEMORY
0498 9D0080 210         STA  SAVETABLE,X
049B AD0140 220         LDA  $4001     
049E 9D0081 230         STA  SAVETABLE+256,X
04A1 8A     240         TXA            
04A2 8D0040 250         STA  $4000     
04A5 18     260         CLC            
04A6 6904   270         ADC  #4        
04A8 8D0140 280         STA  $4001     
04AB E8     290         INX            
04AC D0E4   300         BNE  TESTLP1   
04AE A000   310         LDY  #0        
            320 TESTLP2
04B0 8E01D3 330         STX  PORTB     
04B3 EC0040 340         CPX  $4000     
04B6 D00E   350         BNE  NOTSAME   
04B8 8A     360         TXA            
04B9 18     370         CLC            
04BA 6904   380         ADC  #4        
04BC CD0140 390         CMP  $4001     
04BF D005   400         BNE  NOTSAME   
04C1 8A     410         TXA            
04C2 998004 420         STA  BANKTABLE,Y; ADD TO TABLE
04C5 C8     430         INY            
04C6 E8     440 NOTSAME INX            
04C7 D0E7   450         BNE  TESTLP2   
04C9 88     460         DEY            ; IGNORE MAIN BANK ($FF)
04CA 84C9   470         STY  BANKS     ; # EXPANDED BANKS IN Y
04CC A200   490         LDX  #0        
            500 RESTORE
04CE 8E01D3 510         STX  PORTB     
04D1 BD0080 520         LDA  SAVETABLE,X ; RESTORE OLD VALUES
04D4 8D0040 530         STA  $4000     
04D7 BD0081 540         LDA  SAVETABLE+256,X
04DA 8D0140 550         STA  $4001     
04DD E8     560         INX            
04DE D0EE   570         BNE  RESTORE   
            580 ;
04E0 68     590         PLA            
04E1 8D01D3 600         STA  PORTB     
04E4 68     610         PLA            
04E5 8D0ED4 620         STA  NMIEN     ; RE-ENABLE INTERRUPTS
04E8 58     630         CLI            
            640 ;
            650 ALLDONE
04E9 60     660         RTS            

Link to comment
Share on other sites

Ah. I just look at the Polish one and my mind goes apfjasjhowgio hweitghwiphpsad. Trying to figure out WTF is going on with it at points. Yours seemed straightforward enough. I like to understand what is going on, either by logic or by comments, and the comments did not help me at all there.

 

Let me stare at it a little while longer. Maybe it will make sense then.

Link to comment
Share on other sites

I can't say I worried too much about how it worked. It appeared reliable enough to propagate on the Net, and performed well in tests: therefore I used it.

 

Just found this: interesting reading, should you wish to write your own test:

 

http://web.archive.org/web/20071231110133/http://www.s-direktnet.de/homepages/k_nadj/main.html

 

There is yet another generic routine at the foot of the page, however.

Edited by flashjazzcat
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...