Use GAL as a small eprom

Topics relating to PALs, CPLDs, FPGAs, and other PLDs used for the support or creation of 65-family processors, both hardware and HDL.
User avatar
cbscpe
Posts: 491
Joined: 13 Oct 2013
Location: Switzerland
Contact:

Re: Use GAL as a small eprom

Post by cbscpe »

Here the result of my "investigations". As proposed by bogax only IRQ is used for the data input, A is intialised with 1 to get the carry as the done status for a byte and NMI starts the bootloader. And as I suggested I use an additional input (I call it A5 although it is not connected to the address A5 but to the data input). That selects one of two code options for the IRQ routine (one using SEC and one using CLC). The code itself is position independent so it does not matter that I use $FFC0 as the first starting address for the first incarnation (a macro would do the job much nicer in case you need to change the code, but I was lazy). You need to duplicate the vectors, to make sure they are the same regardless of the state of A5 (data). And also it saves product terms.

Code: Select all

BOOTLOADER = $00


   *=$FFC0
RESETROUTINE0
   LDX #$00   ; Init byte storing index
   LDA #$01   ; Init bit counting 
   BNE	*

IRQROUTINE0   
   CLC   
   ROL   
   BCC FINISH
   STA BOOTLOADER, X   ; Finish one byte of transfer  
   INX   
FINISH0
   RTI 
   *=$FFDA
   
   .word BOOTLOADER
   .word RESETROUTINE
   .word IRQROUTINE

	*=$FFE0
RESETROUTINE
   LDX #$00   ; Init byte storing index
   LDA #$01   ; Init bit counting 
   BNE	*

IRQROUTINE   
   SEC   
   ROL   
   BCC FINISH
   STA BOOTLOADER, X   ; Finish one byte of transfer  
   INX   
FINISH
   RTI 
      
	*=$FFFA
   
   .word BOOTLOADER
   .word RESETROUTINE
   .word IRQROUTINE
   
I use 64TASS to assemble the code and use hexdump to convert it to the table I then use in WinCUPL

Code: Select all

64tass -c -b bootloader.asm -o bootloader.bin
hexdump -e '"[%02.2_ax] => "' -e '1/1 "<h<%02X;" "\n"' -v  bootloader.bin
If someone could tell me the trick to produce ' instead of using the < and then change them all in the editor I would appreciate. But that's only a minor production step. Then I take this output and create the following PLD for WinCUPL

Code: Select all

Name     bootloader ;
PartNo   00 ;
Date     26.07.2015 ;
Revision 01 ;
Designer cbscpe ;
Company  none;
Assembly None ;
Location  ;
Device   g16v8;

/* *************** INPUT PINS *********************/
PIN   2  =  A0                     ; /*                                 */ 
PIN   3  =  A1                     ; /*                                 */ 
PIN   4  =  A2                     ; /*                                 */ 
PIN   5  =  A3                     ; /*                                 */ 
PIN   6  =  A4                     ; /*                                 */ 
PIN   7  =  A5                     ; /*                                 */ 
PIN  11  = !OE                     ; /*                                 */ 

/* *************** OUTPUT PINS *********************/
PIN   12  =  D0                     ; /*                                 */ 
PIN   13  =  D1                     ; /*                                 */ 
PIN   14  =  D2                     ; /*                                 */ 
PIN   15  =  D3                     ; /*                                 */ 
PIN   16  =  D4                     ; /*                                 */ 
PIN   17  =  D5                     ; /*                                 */ 
PIN   18  =  D6                     ; /*                                 */ 
PIN   19  =  D7                     ; /*                                 */ 


FIELD	ADDR = [A5..0];
FIELD DATA = [D7..0];

[D7..0].oe = OE;

TABLE ADDR => DATA {
[00] => 'h'A2;
[01] => 'h'00;
[02] => 'h'A9;
[03] => 'h'01;
[04] => 'h'D0;
[05] => 'h'FE;
[06] => 'h'18;
[07] => 'h'2A;
[08] => 'h'90;
[09] => 'h'23;
[0a] => 'h'95;
[0b] => 'h'00;
[0c] => 'h'E8;
[0d] => 'h'40;
[0e] => 'h'00;
[0f] => 'h'00;
[10] => 'h'00;
[11] => 'h'00;
[12] => 'h'00;
[13] => 'h'00;
[14] => 'h'00;
[15] => 'h'00;
[16] => 'h'00;
[17] => 'h'00;
[18] => 'h'00;
[19] => 'h'00;
[1a] => 'h'00;
[1b] => 'h'00;
[1c] => 'h'E0;
[1d] => 'h'FF;
[1e] => 'h'E6;
[1f] => 'h'FF;
[20] => 'h'A2;
[21] => 'h'00;
[22] => 'h'A9;
[23] => 'h'01;
[24] => 'h'D0;
[25] => 'h'FE;
[26] => 'h'38;
[27] => 'h'2A;
[28] => 'h'90;
[29] => 'h'03;
[2a] => 'h'95;
[2b] => 'h'00;
[2c] => 'h'E8;
[2d] => 'h'40;
[2e] => 'h'00;
[2f] => 'h'00;
[30] => 'h'00;
[31] => 'h'00;
[32] => 'h'00;
[33] => 'h'00;
[34] => 'h'00;
[35] => 'h'00;
[36] => 'h'00;
[37] => 'h'00;
[38] => 'h'00;
[39] => 'h'00;
[3a] => 'h'00;
[3b] => 'h'00;
[3c] => 'h'E0;
[3d] => 'h'FF;
[3e] => 'h'E6;
[3f] => 'h'FF;
}

This is successfully converted to a JED file. Assuming no WinCUPL bug that's it. You can consult the DOC file to see the resulting equations. In other words, yes you can use a GAL as a bootstrap PROM and a GAL16V8 does the job. And as I don't need the SO this will work using either a 6502 or a W65C816. One could even try to use a different bootloader address, there is room enough for another byte for the STA absolute,X instruction and by carefully select the address only a few additional PTs will be used.

Cheers

Peter
User avatar
cbscpe
Posts: 491
Joined: 13 Oct 2013
Location: Switzerland
Contact:

Re: Use GAL as a small eprom

Post by cbscpe »

Oh I found a minor error (a macro would have avoided that). The first BCC must go to FINISH0 else we end at nomansland in zeropage. It's even worse as it wasted an additional PT of the GAL.
User avatar
cbscpe
Posts: 491
Joined: 13 Oct 2013
Location: Switzerland
Contact:

Re: Use GAL as a small eprom

Post by cbscpe »

And another bug (you never find the last but only the second last bug), I need to init A again in the IRQ after a byte has been received.

Code: Select all

;
;	Special Bootloader that fits into a GAL16V8.
;
;	Assemble
;		~/sbc65xxx/64tass/64tass -c -b bootloader.asm -L bootloader.lst -o bootloader.bin
;
;	Convert to table for WinCUPL
;		hexdump -e '"[%02.2_ax] => "' -e '1/1 "%02X;" "\n"' -v  bootloader.bin
;

BOOTLOADER	=	$8000

	*	=	$FFC0
RESETROUTINE0
	LDX		#$00			; Init byte storing index
	LDA		#$01			; Init bit counting 
	BNE		*

IRQROUTINE0   
	CLC   
	ROL   
	BCC		FINISH0
	STA		BOOTLOADER, X	; Finish one byte of transfer  
	INX
	LDA		#1
FINISH0
	RTI 
	*	=	$FFDA
   
	.word	BOOTLOADER
	.word	RESETROUTINE
	.word	IRQROUTINE

	*	=	$FFE0
RESETROUTINE
	LDX		#$00			; Init byte storing index
	LDA		#$01			; Init bit counting 
	BNE		*

IRQROUTINE   
	SEC   
	ROL   
	BCC		FINISH
	STA		BOOTLOADER, X	; Finish one byte of transfer  
	INX   
	LDA		#1
FINISH
	RTI 
      
	*	=	$FFFA
   
   .word	BOOTLOADER
   .word	RESETROUTINE
   .word	IRQROUTINE
   
And this changes of course the table in the PLD a little bit

Code: Select all

Name     bootloader ;
PartNo   00 ;
Date     26.07.2015 ;
Revision 01 ;
Designer csbcpe ;
Company  company;
Assembly None ;
Location  ;
Device   g16v8;

/* *************** INPUT PINS *********************/
PIN   2  =  A0                     ; /*                                 */ 
PIN   3  =  A1                     ; /*                                 */ 
PIN   4  =  A2                     ; /*                                 */ 
PIN   5  =  A3                     ; /*                                 */ 
PIN   6  =  A4                     ; /*                                 */ 
PIN   7  =  A5                     ; /*                                 */ 
PIN  11  = !OE                     ; /*                                 */ 

/* *************** OUTPUT PINS *********************/
PIN   12  =  D0                     ; /*                                 */ 
PIN   13  =  D1                     ; /*                                 */ 
PIN   14  =  D2                     ; /*                                 */ 
PIN   15  =  D3                     ; /*                                 */ 
PIN   16  =  D4                     ; /*                                 */ 
PIN   17  =  D5                     ; /*                                 */ 
PIN   18  =  D6                     ; /*                                 */ 
PIN   19  =  D7                     ; /*                                 */ 


FIELD	ADDR = [A5..0];
FIELD DATA = [D7..0];

[D7..0].oe = OE;

TABLE ADDR => DATA {
[00] => A2;
[01] => 00;
[02] => A9;
[03] => 01;
[04] => D0;
[05] => FE;
[06] => 18;
[07] => 2A;
[08] => 90;
[09] => 06;
[0a] => 9D;
[0b] => 00;
[0c] => 80;
[0d] => E8;
[0e] => A9;
[0f] => 01;
[10] => 40;
[11] => 00;
[12] => 00;
[13] => 00;
[14] => 00;
[15] => 00;
[16] => 00;
[17] => 00;
[18] => 00;
[19] => 00;
[1a] => 00;
[1b] => 80;
[1c] => E0;
[1d] => FF;
[1e] => E6;
[1f] => FF;
[20] => A2;
[21] => 00;
[22] => A9;
[23] => 01;
[24] => D0;
[25] => FE;
[26] => 38;
[27] => 2A;
[28] => 90;
[29] => 06;
[2a] => 9D;
[2b] => 00;
[2c] => 80;
[2d] => E8;
[2e] => A9;
[2f] => 01;
[30] => 40;
[31] => 00;
[32] => 00;
[33] => 00;
[34] => 00;
[35] => 00;
[36] => 00;
[37] => 00;
[38] => 00;
[39] => 00;
[3a] => 00;
[3b] => 80;
[3c] => E0;
[3d] => FF;
[3e] => E6;
[3f] => FF;

}

Also just found out that HEX is the default in WinCUPL so I don't need the 'h' prefix. In any case the design still fits, now one output requires 6 PTs, so still one left for a further bug.

Cheers

Peter
i_r_on
Posts: 62
Joined: 20 Jul 2015

Re: Use GAL as a small eprom

Post by i_r_on »

@cbscpe : What does BNE * does? I'm not familiar with that syntax.. Is it just a placeholder for page wrapping branch?
User avatar
cbscpe
Posts: 491
Joined: 13 Oct 2013
Location: Switzerland
Contact:

Re: Use GAL as a small eprom

Post by cbscpe »

* is place holder for the program counter in this assembler. In other words

Code: Select all

      BNE    *
is the same as

Code: Select all

HERE    BNE    HERE
It's just the eternal loop of the main program.
i_r_on
Posts: 62
Joined: 20 Jul 2015

Re: Use GAL as a small eprom

Post by i_r_on »

I just missed the point that single NMI invocation actually was somewhat jump to the loaded bootloader. Now it makes sense.
User avatar
Dr Jefyll
Posts: 3525
Joined: 11 Dec 2009
Location: Ontario, Canada
Contact:

Re: Use GAL as a small eprom

Post by Dr Jefyll »

cbscpe wrote:
an additional input [...] selects one of two code options for the IRQ routine (one using SEC and one using CLC).
I like this bit especially. The "memory" changes its apparent contents, from SEC to CLC, according to a signal from an external source. This eliminates any need for the program to test that signal in the usual fashion. The program outcome is implicitly modified by the SEC or CLC. Nice work on that (and the WinCUPL), Peter!

It's not a technique limited to GALs. The latter part of this post (after "Slightly OT") describes an EPROM that changes its apparent contents according to a signal from an external source.

-- Jeff
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html
i_r_on
Posts: 62
Joined: 20 Jul 2015

Re: Use GAL as a small eprom

Post by i_r_on »

@cbscpe : You don't clear interrupt flag at the beginning of the code. IRQs will be inhibited that way.

Here is my take to the stuff. I didn't want to introduce another input to the GAL as I would be using S.O. anyway for a little bit of speed.

Here is my code, S.O. signals end of each byte, IRQ and NMI stores 1 and 0 into accumulator respectively. I also manually modified PLD source for WinCUPL to optimize it. The issue is, I didn't get the expected result out of it. I don't have 65c02 so single stepping is a big no. I used the trick previously I used to test rom & ram access. I only have 32k ram, ROM decodes to $E000-$FFFF, Ram decodes to $0000-$7FFF. I have a 3 to 8 decoder so I use additional decoding signals to interrupt arduino. $8000-$9FFF -> Int 0, $A000-$BFFF -> Int 1. I do access these locations to transmit information to arduino. This previously worked well and what I used to test my apartment style boards for errors.

Can you spot any apparent error in below setup?

Contents of boot loader (6502Bootloaderx.65s)

Code: Select all

BOOTLOADER = $00


	*=$FFE0
	.BYTE 0, 0, 0, 0, 0, 0, 0, 0 ; Will be replaced by Do not care bytes.
RESETROUTINE
	LDX #$00	; Init byte storing index
	CLI		; Enable Interrupts
	SEC		; Set Carry for IRQ (IRQ pulls from stack)
NEXT
	CLV
	BVC *
	STA BOOTLOADER,X
	INX
	BNE NEXT
	JMP BOOTLOADER
		
NMIROUTINE	
	ASL
	RTI
	
IRQROUTINE	
	ROL
	RTI	
				
	*=$FFFA
	
	.WORD NMIROUTINE
	.WORD RESETROUTINE
	.WORD IRQROUTINE	
	
PLD code (Data pins reversed as I created a board out of this and aligned Data pins to 6502 data bus locations)

Code: Select all

Name     bootloader ;
PartNo   00 ;
Date     27.07.2015 ;
Revision 01 ;
Designer i_r_on ;
Company  none;
Assembly None ;
Location  ;
Device   g16v8;

/* *************** INPUT PINS *********************/
PIN   2  =  A0                     ; /*                                 */ 
PIN   3  =  A1                     ; /*                                 */ 
PIN   4  =  A2                     ; /*                                 */ 
PIN   5  =  A3                     ; /*                                 */ 
PIN   6  =  A4                     ; /*                                 */ 
PIN  11  = !OE                     ; /*                                 */ 

/* *************** OUTPUT PINS *********************/
PIN   12  =  D7                     ; /*                                 */ 
PIN   13  =  D6                     ; /*                                 */ 
PIN   14  =  D5                     ; /*                                 */ 
PIN   15  =  D4                     ; /*                                 */ 
PIN   16  =  D3                     ; /*                                 */ 
PIN   17  =  D2                     ; /*                                 */ 
PIN   18  =  D1                     ; /*                                 */ 
PIN   19  =  D0                     ; /*                                 */ 

FIELD   ADDR = [A4..0];
FIELD DATA = [D7..0];

[D7..0].oe = OE;

TABLE ADDR => DATA {
[00] => 'h'xx;
[01] => 'h'xx;
[02] => 'h'xx;
[03] => 'h'xx;
[04] => 'h'xx;
[05] => 'h'xx;
[06] => 'h'xx;
[07] => 'h'xx;
[08] => 'h'a2;
[09] => 'h'00;
[0a] => 'h'58;
[0b] => 'h'38;
[0c] => 'h'b8;
[0d] => 'h'50;
[0e] => 'h'fe;
[0f] => 'h'95;
[10] => 'h'00;
[11] => 'h'e8;
[12] => 'h'd0;
[13] => 'h'f8;
[14] => 'h'4c;
[15] => 'h'0x;
[16] => 'h'00;
[17] => 'h'0a;
[18] => 'h'40;
[19] => 'h'2a;
[1a] => 'b'xxx10111;
[1b] => 'b'11xxxxxx;
[1c] => 'h'xxx01000;
[1d] => 'b'11xxxxxx;
[1e] => 'h'xxx11001;
[1f] => 'b'11xxxxxx;
}

Second stage loader of the test routine (2ndBootloader.65s)

Code: Select all

PAYLOADADDRESS = $7000
	*=$0000
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	NOP
	SEI
	CLV
	CLD
	LDX #$FF
	TXS	
	CLI
	INX
NEXT	
	LDA PAYLOAD,X
	STA PAYLOADADDRESS,X
	INX
	BNE NEXT
	JMP PAYLOADADDRESS

	*=$002F
	.BYTE 0
PAYLOAD	
	;Actual Payload will be appended here...
	;Above code copies payload to PAYLOADADDRESS and controls
	;transfer there.

Actual test routine (MainPayload.65s)

Code: Select all

	*=$7000	

					 	
;TEST STACK ACCESS
	LDA #$99
	PHA
	LDA #$00
	PLA 
	CMP #$99
	BNE FAIL
	JSR SENDBYTE ; Send $99 as a success status for stack test
	JMP ZEROPAGETEST
FAIL
	LDA #$CC
	JSR SENDBYTE 

ZEROPAGETEST
	LDX #00
	; Write $77 through $00 to $FF
	LDA #$77
NEXTLOC	
	STA $00, X
	INX
	BNE NEXTLOC
	; Try to read $77 through $00 to $FF	
	LDA #$00
	LDX #$00
NEXTLOCREAD
	LDA $00, X
	CMP #$77
	BNE FAILZEROPAGE
	INX
	BNE NEXTLOCREAD
	
	; Zero page test success
	LDA #$99 
	JSR SENDBYTE
	JMP MEMTEST
	; Zero page test fail
FAILZEROPAGE
	LDA #$CC
	JSR SENDBYTE

MEMTEST
	LDY #$00
	LDA #$00
	STA $FB
	LDA #$7F
	STA $FC
NEXTPAGE
	LDA #$01
INCURPAGE
	STA ($FB), Y
	CLC	
	ADC #$01
	INY
	BNE INCURPAGE
	DEC $FC
	BNE NEXTPAGE

; Read to test
	LDY #$00
	LDA #$00
	STA $FB
	LDA #$7F
	STA $FC
NEXTPAGEREAD
	LDA #$01
INCURPAGEREAD
	CMP ($FB), Y	
	BNE MEMTESTFAIL
	CLC
	ADC #$01
	INY
	BNE INCURPAGEREAD
	DEC $FC
	BNE NEXTPAGEREAD

	LDA #$99
	JSR SENDBYTE
	JMP ENDLESS	
MEMTESTFAIL
	LDA #$CC
	JSR SENDBYTE
	CLV
ENDLESS	
	BVC ENDLESS


SENDBYTE 
NEXT
	LDY #$08	
SINGLEBITSEND
	LDX #$40
CONSUMETIME
	DEX
	BNE CONSUMETIME
	ROL
	BCS ONE
	STA $8000	
	JMP NEXTBIT	
ONE
	STA $A000
NEXTBIT
	DEY
	BNE SINGLEBITSEND	
	RTS

So I compile all of this to create under 256 bytes code like this,

Code: Select all

64tass -c -b 6502BootLoader3.65s -o 6502BootLoader3.65s.bin
64tass -c -b 2ndbootloader.65s -o 2ndbootloader.65s.bin
64tass -c -b MainPayload.65s -o MainPayload.65s.bin
copy 2ndBootloader.65s.bin + MainPayload.65s.bin Bootloader.bin /b
bin2h  -cz bootloader <Bootloader.bin > Bootloader.h
And here is relevant part of my arduino code,

Code: Select all

void TransmitByteSpecial(unsigned char val) {    
    unsigned char mask = 0x80;
        
    for (int i = 0; i<8; i++) {
        if (val & mask) {
            //Transmit 1
            digitalWrite(IRQ, LOW);
            delayMicroseconds(5); //Wait 5 micro seconds for interrupt to trigger (approx. 5 cycles)
            digitalWrite(IRQ, HIGH);
            delayMicroseconds(40);  //Wait 40 micro seconds for interrupt to finish it's job (approx. 40 cycles)

        } else {
            //Transmit 0
            digitalWrite(NMI, LOW);
            delayMicroseconds(5); //Wait 5 micro seconds for interrupt to trigger (approx. 5 cycles)
            digitalWrite(NMI, HIGH);
            delayMicroseconds(40);  //Wait 40 micro seconds for interrupt to finish it's job (approx. 40 cycles)

        }
        mask = mask>>1;
    }
    
    //  [Interrupt enclosure end(wait before byte storage)]
    digitalWrite(SO, LOW);
    delayMicroseconds(5); //Wait LOOP BVC  LOOP on 6502 side
    digitalWrite(SO, HIGH);      
    delayMicroseconds(100); 
}

void LoaderTest2() {
    delay(1000);    
    attachInterrupt(0, serialInputZero ,FALLING);      
    attachInterrupt(1, serialInputOne  ,FALLING);      
    delay(1000);
    Reset6502();    
    delay(250);    
    for (int i = 0;i<256;i++) {
      if (i<bootloader_size) {
        TransmitByteSpecial(bootloader[i]);
      } else {
        TransmitByteSpecial(0);
      }
    }
    
    delay(5000);
    Serial.println();
    Serial.println(F("Test is dones, detaching interrupts!"));            
    detachInterrupt(0);      
    detachInterrupt(1);         
}



User avatar
cbscpe
Posts: 491
Joined: 13 Oct 2013
Location: Switzerland
Contact:

Re: Use GAL as a small eprom

Post by cbscpe »

Hi i_r_on

thanks for spotting the missing CLI. Fortunately this still fits in the design of the GAL. I will however stick with my solution, as I don't have a 6502 but only a 65C816 which has no SO. In any case, I went over your code and I think this will work.

You say you did not get the expected result, what was the issue? Looking at the many don't cares you have added I assume you had excess number of product terms for some bits. Good trick.

I have one suggestion for your newest solution, why do you still stick to the three phase bootstrap? Now as you have some bytes left you could place BOOTLOADER wherever you want. So you do not have to move the code around. I tried with BOOTLOADER = $7000 and it still fits into the design (although all 7 PTs of D3, D4, D5 and D6 are used).

Cheers

Peter
i_r_on
Posts: 62
Joined: 20 Jul 2015

Re: Use GAL as a small eprom

Post by i_r_on »

@cbspe : I guess I found my problem. I overuse don't care bits I think.

Code: Select all

[1a] => 'b'xxx10111;  /* fits into A0-A7 so it's ok for A7, A6 and A5 to have any value */
[1b] => 'b'11xxxxxx;  /* I decode CS for ROM as $E000-$FFFF. I should have written : 'b'111xxxxx' */
Actually for second stage loader thing I thought it like you but getting PLD code to compile successfully was a bit of trial and error. I just postponed using a bigger payload loader.

If it fails compiling, how do you see which other PTs are used the least?
User avatar
cbscpe
Posts: 491
Joined: 13 Oct 2013
Location: Switzerland
Contact:

Re: Use GAL as a small eprom

Post by cbscpe »

H i_r_on,

in the .doc file created by WinCUPL you see at the bottom a summary of the PTs used, here I only show a extract of it. Have a look at it it is very informative

Code: Select all

===============================================================================
                                 Symbol Table
===============================================================================

Pin Variable                                    Pterms   Max     Min    
Pol   Name              Ext     Pin     Type     Used   Pterms  Level   
--- --------            ---     ---     ----    ------  ------  -----   

    A0                          2        V        -       -       -     
    A1                          3        V        -       -       -     
    A2                          4        V        -       -       -     
    A3                          5        V        -       -       -     
    A4                          6        V        -       -       -     
    A5                          7        V        -       -       -     
    ADDR                        0        F        -       -       -     
    D0                          12       V        4       7       2     
    D0                  oe      12       X        1       1       1     
    D1                          13       V        4       7       2     
    D1                  oe      13       X        1       1       1     
    D2                          14       V        3       7       2     
    D2                  oe      14       X        1       1       1     
    D3                          15       V        7       7       2     
    D3                  oe      15       X        1       1       1     
    D4                          16       V        7       7       2     
    D4                  oe      16       X        1       1       1     
    D5                          17       V        7       7       2     
    D5                  oe      17       X        1       1       1     
    D6                          18       V        7       7       2     
    D6                  oe      18       X        1       1       1     
    D7                          19       V        5       7       2     
    D7                  oe      19       X        1       1       1     
    DATA                        0        F        -       -       -     
 !  OE                          11       V        -       -       -     


LEGEND    D : default variable         F : field      G : group
          I : intermediate variable    N : node       M : extended node
          U : undefined                V : variable   X : extended variable
          T : function

You actually see also the created equations and more useful stuff. As you see there are in fact 8 PTs per OLMC, but as we need one for the /OE this leaves only 7 PTs for the logic. Please be aware that between compile runs of WinCUPL the .doc file is not updated in the editor should have it open. You need to close and re-open it to see the results of the latest run. Just close it and open it by double-clicking on the .doc file in the navigator window at the right hand of the screen. Also you can observe the usage by shifting using different start addresses for the various routines. So you can switch the order of the RESET, NMI and IRQ routine and you could also check if placing dummy bytes in between helps reducing the number of PTs used.

I would be interested if you manage to put a bootloader into the GAL that allows more than 256bytes. Perhaps using STA (PTR),Y.


Cheers

Peter
User avatar
cbscpe
Posts: 491
Joined: 13 Oct 2013
Location: Switzerland
Contact:

Re: Use GAL as a small eprom

Post by cbscpe »

Hi i_r_on

if you want to analyze the usage of PTs you can replace the device "g16v8" with "virtual". This will create a DOC file with all equations regardless how many PTs would be used. Of course no JED is created. But to find what needs to be optimized this is the easiest way to find which outputs would require more than the available 7 PTs.

Peter
i_r_on
Posts: 62
Joined: 20 Jul 2015

Re: Use GAL as a small eprom

Post by i_r_on »

cbscpe wrote:
I would be interested if you manage to put a bootloader into the GAL that allows more than 256bytes. Perhaps using STA (PTR),Y.
Cheers

Peter
Yes that would be possible using Y indirect indexed addressing. I'll see if I can optimize it further with that virtual device option. But it would be very difficult I guess with 16v8.
User avatar
cbscpe
Posts: 491
Joined: 13 Oct 2013
Location: Switzerland
Contact:

Re: Use GAL as a small eprom

Post by cbscpe »

Hi i_r_on,

using my approach, with one bit modifying the instruction and using the NMI to jump to the loaded ROM the following code fits in 32 bytes and the resulting design fits into a GAL16V8.

Code: Select all

;
;	Special Bootloader that fits into a GAL16V8.
;
;	Assemble
;		~/sbc65xxx/64tass/64tass -c -b bootloaderv3.asm -L bootloaderv3.lst -o bootloaderv3.bin
;
;	Convert to table for WinCUPL
;		hexdump -e '"[%02.2_ax] => "' -e '1/1 "%02X;" "\n"' -v  bootloaderv3.bin
;

BOOTLOADER	=	$7000
PTR			=	$0

	*	=	$FFC0
	
RESETROUTINE0
	LDY		#0						; Init Index Register
	STY		PTR						; Set low byte of pionter
	LDA		#>BOOTLOADER			; Get High byte of ROM
	STA		PTR+1					; Set high byte of pointer
	LDA		#1						; Init Akku
	CLI								; Enable Interrupts
	BNE		*						; And wait forever

IRQROUTINE0   
	ASL								; Two incarnation this is 0 bit
	BCC		FIN0					; Not yet done a byte
	STA		(PTR), Y				; Store byte
	LDA		#1						; Init Akku
	INY								; Next Index
	BNE		FIN0					; No overflow
	INC		PTR+1					; Increment high byte of pointer
FIN0								; do no overflow check (no space)
	RTI								; Done

	*	=	$FFDA

	.word	BOOTLOADER   
	.word	RESETROUTINE
	.word	IRQROUTINE
;
;
;
	*	=	$FFE0
RESETROUTINE 
	LDY		#0						; Init Index Register
	STY		PTR						; Set low byte of pionter
	LDA		#>BOOTLOADER			; Get High byte of ROM
	STA		PTR+1					; Set high byte of pointer
	LDA		#1						; Init Akku
	CLI								; Enable Interrupts
	BNE		*						; And wait forever

IRQROUTINE  
	ROL								; Two incarnation this is 0 bit
	BCC		FIN 					; Not yet done a byte
	STA		(PTR), Y				; Store byte
	LDA		#1						; Init Akku
	INY								; Next Index
	BNE		FIN 					; No overflow
	INC		PTR+1					; Increment high byte of pointer
FIN 								; do no overflow check (no space)
	RTI								; Done
	*	=	$FFFA
	
	.word	BOOTLOADER
	.word	RESETROUTINE
	.word	IRQROUTINE
It's really a tight fit. But now I start to ask myself what happens after the image has been loaded and executed and an interrupt occurs. If I understand your design correct $E000..$FFFF always is the ROM which is the GAL. Or do you have some other mechanism? I would have added another GAL as an address decoder and a 128kbyte RAM instead of a 32kbyte RAM. I would also add a BOOT output to the arduino that changes the decoder. If the bit is set, reads always enable the GAL and writes enable always the RAM. If the bit is cleared the GAL is disabled and except for the IO range the RAM is enabled, optionally write-protect the upper part. Once you clear the bit another reset would then use the reset vector in the program image loaded into the RAM.

Peter
rwiker
Posts: 294
Joined: 03 Mar 2011

Re: Use GAL as a small eprom

Post by rwiker »

cbscpe wrote:
Hi i_r_on,

using my approach, with one bit modifying the instruction and using the NMI to jump to the loaded ROM the following code fits in 32 bytes and the resulting design fits into a GAL16V8.

Code: Select all

;
;	Special Bootloader that fits into a GAL16V8.
;
;	Assemble
;		~/sbc65xxx/64tass/64tass -c -b bootloaderv3.asm -L bootloaderv3.lst -o bootloaderv3.bin
;
;	Convert to table for WinCUPL
;		hexdump -e '"[%02.2_ax] => "' -e '1/1 "%02X;" "\n"' -v  bootloaderv3.bin
;

BOOTLOADER	=	$7000
PTR			=	$0

	*	=	$FFC0
	
RESETROUTINE0
	LDY		#0						; Init Index Register
	STY		PTR						; Set low byte of pionter
	LDA		#>BOOTLOADER			; Get High byte of ROM
	STA		PTR+1					; Set high byte of pointer
	LDA		#1						; Init Akku
	CLI								; Enable Interrupts
	BNE		*						; And wait forever

IRQROUTINE0   
	ASL								; Two incarnation this is 0 bit
	BCC		FIN0					; Not yet done a byte
	STA		(PTR), Y				; Store byte
	LDA		#1						; Init Akku
	INY								; Next Index
	BNE		FIN0					; No overflow
	INC		PTR+1					; Increment high byte of pointer
FIN0								; do no overflow check (no space)
	RTI								; Done

	*	=	$FFDA

	.word	BOOTLOADER   
	.word	RESETROUTINE
	.word	IRQROUTINE
;
;
;
	*	=	$FFE0
RESETROUTINE 
	LDY		#0						; Init Index Register
	STY		PTR						; Set low byte of pionter
	LDA		#>BOOTLOADER			; Get High byte of ROM
	STA		PTR+1					; Set high byte of pointer
	LDA		#1						; Init Akku
	CLI								; Enable Interrupts
	BNE		*						; And wait forever

IRQROUTINE  
	ROL								; Two incarnation this is 0 bit
	BCC		FIN 					; Not yet done a byte
	STA		(PTR), Y				; Store byte
	LDA		#1						; Init Akku
	INY								; Next Index
	BNE		FIN 					; No overflow
	INC		PTR+1					; Increment high byte of pointer
FIN 								; do no overflow check (no space)
	RTI								; Done
	*	=	$FFFA
	
	.word	BOOTLOADER
	.word	RESETROUTINE
	.word	IRQROUTINE
It's really a tight fit. But now I start to ask myself what happens after the image has been loaded and executed and an interrupt occurs. If I understand your design correct $E000..$FFFF always is the ROM which is the GAL. Or do you have some other mechanism? I would have added another GAL as an address decoder and a 128kbyte RAM instead of a 32kbyte RAM. I would also add a BOOT output to the arduino that changes the decoder. If the bit is set, reads always enable the GAL and writes enable always the RAM. If the bit is cleared the GAL is disabled and except for the IO range the RAM is enabled, optionally write-protect the upper part. Once you clear the bit another reset would then use the reset vector in the program image loaded into the RAM.

Peter
If I have read the instruction tables right, there is only a single bit difference between the opcode for ASL and ROL (and also between CLC and SEC that you used in an earlier version). Would it be possible to have the input bit that chose between the two instruction sequences instead specify the single bit that differs between the two instructions (and thus between the two instruction sequences)?

Not sure if this makes for less resource usage in the PAL, though: you still have the same number of inputs, and the same number of output patterns.
Post Reply