Jump to content
Sign in to follow this  
oPossum

Game of Life with G2211

Recommended Posts


// main.c

#include "msp430g2211.h"
#include "string.h"

void video_init(void);

unsigned pix[14], nxt[14];

unsigned get_pix(int x, int y)
{
   if(x < 0 || x > 15 || y < 0 || y > 13) return 0;
   return (pix[y] >> x) & 1;
}

void main(void)
{
   int x, y, n;

   WDTCTL = WDTPW + WDTHOLD; // No watchdog

   BCSCTL1 = XT2OFF + 15;  // DCO 16 Mhz
   DCOCTL = 0x86;          //
                           //
   P1DIR = 0xF2;           // I/O assignment
   P1REN = 0x0D;           // Pull up resistor
   P1OUT = 0x0F;           // 
   P1SEL = 0x50;           // Enable Timer A output, SMCLK output

   video_init();           // Begin video generation

                           // Initial GOL field
   pix[0] = pix[2] = pix[4] = pix[6] = pix[8] = pix[10] = pix[12] = 0x5555;
   pix[1] = pix[3] = pix[5] = pix[7] = pix[9] = pix[11] = pix[13] = 0xAAAA;
   pix[0] = 0x0000;
   pix[1] = 0x000E;
   pix[2] = 0x0000;
   pix[3] = 0;

   memcpy(nxt, pix, sizeof(nxt));
   for(; {
       for(y = 0; y < 14; ++y) {
           for(x = 0; x < 16; ++x) {
               n = get_pix(x - 1, y - 1) + get_pix(x    , y - 1) + get_pix(x + 1, y - 1) +
                   get_pix(x - 1, y    ) +     /* me */            get_pix(x + 1, y    ) +
                   get_pix(x - 1, y + 1) + get_pix(x    , y + 1) + get_pix(x + 1, y + 1);
               if(get_pix(x, y)) {
                   if(n < 2 || n > 3) nxt[y] &= ~(1 << x);
               } else {
                   if(n == 3) nxt[y] |= (1 << x);
               }
           }
       }
       __delay_cycles(5000000);
       memcpy(pix, nxt, sizeof(pix));
   }
}


; video.asm
       .cdecls C, LIST, "msp430g2211.h"

       .text
       .global video_init
       .global pix

       .bss    sync_state, 2
       .bss    sync_lines, 2
       .bss    video_state, 2
       .bss    vid_line, 2


video_init                              ; --- Initialize Video Generation ---
       clr     &sync_state             ; Init state machine
       mov     #1, sync_lines          ; 
       clr     &video_state            ;
       mov     #1016, &TACCR0          ; Setup Timer A period for 63.5 us
       mov     #75, &TACCR1            ; Setup Timer A compare for video sync
       mov     #0x0210, &TACTL         ; Timer A config: SMCLK, count up
       mov     #0x0060, &TACCTL1       ; Setup Timer A set/reset output mode
       bis     #0x0010, &TACCTL0       ; Enable PWM interupt
       eint                            ; Enable interupts
       ret                             ;
                                       ;
video_isr                               ; --- Video ISR ---
       dec     &sync_lines             ; Decrement sync lines
       jne     vid_state               ; Skip sync if not zero...
       add     &sync_state, PC         ; Jump to sync handler...
                                       ; - Field 1/3
       jmp     vid_eq                  ; Pre Equalizing 
       jmp     vid_vsync               ; Vertical Sync
       jmp     vid_eq                  ; Post Equalizing
       jmp     vid_vbi                 ; VBI
       jmp     vid_blank_9             ; 9 blank lines
       jmp     vid_active              ; Active Video
       jmp     vid_blank_9             ; 9 blank lines
       jmp     vid_half                ; Video half line
                                       ; - Field 2/4
       jmp     vid_eq                  ; Pre Equalizing
       jmp     vid_vsync               ; Vertical Sync
       jmp     vid_eq                  ; Post Equalizing
       jmp     vid_half                ; Video half line
       jmp     vid_vbi                 ; VBI
       jmp     vid_blank_9             ; 9 blank lines
       jmp     vid_active              ; Active Video
       jmp     vid_blank_9l            ; 9 blank lines
vid_state                               ; -- Active Video Generation
       add     &video_state, PC        ;
       reti                            ; No active video
       jmp     vid_field               ; Field
                                       ;       
vid_eq                                  ; Equalizing pulses - 6 half lines
       mov     #508, &TACCR0           ; Half line
       mov     #38, &TACCR1            ; Equalizing pulse duration
       mov     #6, &sync_lines         ; 6 half lines
       clr     &video_state            ; No video generation during sync
       incd    &sync_state             ; Next state
       reti                            ;
                                       ;                                       
vid_vsync                               ; Vertical sync - 6 half lines
       mov     #508 - 75, &TACCR1      ; Sync pulse duration
       mov     #6, &sync_lines         ; 6 half lines
       incd    &sync_state             ; Next state
       reti                            ;
                                       ;
vid_half                                ; Video half line - 1 half line
       mov     #508, &TACCR0           ; Half line
       mov     #75, &TACCR1            ; Sync pulse duration
       mov     #1, &sync_lines         ; 1 half line
       incd    &sync_state             ; Next state
       clr     &video_state            ; No video generation during half line and sync
       reti                            ;
                                       ;
vid_vbi                                 ; Vertical blanking interval - 11 lines
       mov     #1016, &TACCR0          ; Full line
       mov     #75, &TACCR1            ; Sync pulse duration
       mov     #11, &sync_lines        ; 11 lines
       incd    &sync_state             ; Next state
       reti                            ;
                                       ;
vid_active                              ; Active video - 224 lines
       mov     #224, &sync_lines       ; 224 lines
       incd    &sync_state             ; Next state
       mov     #2, &video_state        ; Genearte video
       clr     &vid_line               ; Clear video line count
       jmp     vid_field               ; Generate video...
                                       ;
vid_blank_9                             ; 9 blank lines
       mov     #9, &sync_lines         ;
       incd    &sync_state             ;
       clr     &video_state            ;
       reti                            ;
                                       ;
vid_blank_9l                            ; 9 blank lines (last in field)
       mov     #9, &sync_lines         ;
       clr     &sync_state             ;
       clr     &video_state            ;
       reti                            ;
                                       ;
vid_field                               ;
       push    R10                     ;
       push    R12                     ;
       push    R14                     ;
                                       ;
       mov     &TAR, R10               ;
       cmp     #176, R10               ;
       jlo     $ - 8                   ;
       and     #7, R10                 ;
       rla     R10                     ;
       add     R10, PC                 ;
       nop                             ;
       nop                             ;
       nop                             ;
       nop                             ;
       nop                             ;
       nop                             ;
       nop                             ;
                                       ;
       mov     &vid_line, R12          ;
       inc     &vid_line               ;
       rra     R12                     ;
       rra     R12                     ;
       rra     R12                     ;
       ;bic    #1, R12                 ;
       add     #pix, R12               ;
       mov     @R12, R12               ;
       mov     #16, R14                ;
pixloop rlc     R12                     ;
       jc      pixon                   ;
       bic.b   #0x80, &P1OUT           ;
       jmp     nexpix                  ;
pixon   bis.b   #0x80, &P1OUT           ;
       jmp     nexpix                  ;
nexpix  mov     #10, R10                ;
       dec     R10                     ;
       jne     $ - 2                   ;
       dec     R14                     ;
       jne     pixloop                 ;
       nop                             ;
       nop                             ;
       nop                             ;
       bic.b   #0x80, &P1OUT           ;
                                       ;
       pop     R14                     ;
       pop     R12                     ;
       pop     R10                     ;
                                       ;
       reti                            ;
                                       ;
                                       ; Interrupt Vectors
       .sect   ".int09"                ; TACCR0 CCIFG
       .short  video_isr               ;
                                       ;
       .end                            ;



I can explain the video code if anyone is interested. It is very flexible and does proper interlaced video and sync (equalization and serration).

Share this post


Link to post
Share on other sites

As I said in my previous post, it would be great to put that video generation code to some serious (and not so serious) use.
So I took oPossum's asm code, added some ADC stuff, and here it is, 46" 4ch 10bit binary meter icon_e_smile.gif




For those interested in msp video games, checkout slaa177

Share this post


Link to post
Share on other sites

An improved meter.
This is fun, it's like getting a new toy.
oPossum, here's a challenge for you. Change your asm to take sync, not generate, so we can overlay video.



#include "msp430g2231.h"
#include "string.h"

void video_init(void);

unsigned char charIndex = 0;
unsigned char rowIndex = 0;
unsigned int adcValue = 0;
unsigned int digit = 0;

unsigned int pix[14] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0};
unsigned int tmp[5] = {0,0,0,0,0};
unsigned const int digits[5][10] = {{0x7000,0x2000,0x7000,0x7000,0x5000,0x7000,0x7000,0x7000,0x7000,0x7000},
{0x5000,0x6000,0x1000,0x1000,0x5000,0x4000,0x4000,0x1000,0x5000,0x5000},
{0x5000,0x2000,0x7000,0x7000,0x7000,0x7000,0x7000,0x1000,0x7000,0x7000},
{0x5000,0x2000,0x4000,0x1000,0x1000,0x1000,0x5000,0x1000,0x5000,0x1000},
{0x7000,0x2000,0x7000,0x7000,0x1000,0x7000,0x7000,0x1000,0x7000,0x7000}};

void main(void) {
WDTCTL = WDTPW + WDTHOLD; // No watchdog

BCSCTL1 = XT2OFF + 15; // DCO 16 Mhz
DCOCTL = 0x86; //

ADC10CTL0 = ADC10SHT_2 + ADC10ON + ADC10IE;
ADC10CTL1 = INCH_1; //
ADC10AE0 |= BIT1; //

P1DIR = 0xF0; // I/O assignment
P1SEL = 0x50; // Enable Timer A output, SMCLK output

video_init(); // Begin video generation

for(; {
__delay_cycles(1000000);

adcValue = ADC10MEM;

charIndex = 0;
while(charIndex < 4) {
digit = adcValue % 10;
adcValue /= 10;
rowIndex = 0;
while(rowIndex < 5) {
tmp[rowIndex] >>= 4;
tmp[rowIndex] |= digits[rowIndex][digit];
rowIndex++;
}
charIndex++;
}

memcpy(pix, tmp, sizeof(tmp));
ADC10CTL0 |= ENC + ADC10SC;
}
}

// ADC10 interrupt service routine
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR(void) {
ADC10CTL0 &= ~ENC;
}

Share this post


Link to post
Share on other sites

I think video overlay is best done by dedicated chips. Creating a MCU clock that is phase locked to the video sync and/or colorburst is non-trivial. The video will shake (jitter) without phase lock. I have used the Zilog Z86229 chip and it works well.

 

Thanks for the DVM code. I have modified it to write directly to the video buffer and not use division or mod (they are slow and use a lot of code). The font has been packed in to fewer words.

 

#include "msp430g2231.h"
#include "string.h"

void video_init(void);

unsigned pix[14];

static const unsigned digits[5][3] = {
   //  0123    4567    89--
   { 0x7277, 0x5777, 0x7700 },
   { 0x5611, 0x5441, 0x5500 },
   { 0x5277, 0x7771, 0x7700 },
   { 0x5241, 0x1151, 0x5100 },
   { 0x7277, 0x1771, 0x7700 }
};

void main(void)
{
   unsigned adc;
   int pos, shift;
   register unsigned digit, mask;
   register unsigned *pp;
   register const unsigned *dp;

   WDTCTL = WDTPW + WDTHOLD;       // No watchdog

   BCSCTL1 = XT2OFF + 15;          // DCO 16 Mhz
   DCOCTL = 0x86;                  //

   ADC10AE0 = BIT1;                // Analog in on P1.1 (A1)
   ADC10CTL1 = INCH_1;             // Select A1
                                   // Configure ADC
   ADC10CTL0 = ADC10SHT_2 | ADC10SR | ADC10ON | ENC; 

   P1DIR = 0xF0;                   // I/O assignment
   P1SEL = 0x50;                   // Enable Timer A output, SMCLK output

   memset(pix, 0, sizeof(pix));    // Clear screen
   video_init();                   // Begin video generation

   for(; {
       __delay_cycles(500000);

       ADC10CTL0 |= ADC10SC;       // Start ADC conversion
       while(!(ADC10CTL0 & ADC10IFG)); // Wait for conversion complete
       adc = ADC10MEM;             // Get result

                                   // Show on screen
       mask = 0xF000; pos = 0; do {
           digit = 0; while(adc >= 1000) ++digit, adc -= 1000; adc = (adc + (adc << 2)) << 1;
           shift = (pos++ - (digit & 3)) << 2; dp = &digits[0][digit >> 2]; pp = pix + 4;
           digit = 5; while(digit--) *pp = (~mask & *pp) | \
               (mask & ((shift < 0) ? (*dp << -shift) : (*dp >> shift))), ++pp, dp += 3;
       } while(mask >>= 4);
   }
}

Share this post


Link to post
Share on other sites

Here is the video code written in C for those who don't understand assembly.

 

The ISR uses a state machine to change the sync timing as the frame progresses. The Timer A compare registers are updated as needed to produce proper NTSC sync timing. A few states will also do setup for the actual video generation. This method is very simple and flexible.

 

The C code was a bit too slow to get the equalization pulse timing exact, so the sync pulse is a litter wider than it should be (50 clocks vs. 38 clocks).

 

The ISR...

__interrupt void video_isr(void)                //  Video ISR
{                                               //
   if(--sync_lines) {                          // Decrement the sync line count, if not zero yet then
       if(video_state) active_video();         //  generate video if video_state is non-zero
   } else {                                    // Sync line count is now zero, so update the sync timing
       switch(sync_state) {                    //
           default:                            // Restart state machine
               sync_state = 0;                 //
           case 0: case 2: case 8: case 10:    // Equalizing pulses - 6 half lines
               TACCR1 = 50; TACCR0 = 508;  sync_lines = 6; break;
           case 1: case 9:                     // Vertical sync - 6 half lines
               TACCR1 = 508 - 75;          sync_lines = 6; break;
           case 3: case 12:                    // VBI - 11 lines
               TACCR1 = 75; TACCR0 = 1016; sync_lines = 11; break;
           case 4: case 6: case 13: case 15:   // 9 blank lines 
                                           sync_lines = 9;   video_state = 0; break;
           case 5: case 14:                    // Active video - 224 lines
                                           sync_lines = 224; video_state = 1; vid_line = 0; active_video(); break;
           case 7: case 11:                    // Half line - 1 half line
               TACCR1 = 75; TACCR0 = 508;  sync_lines = 1; break;
       }                                       //
       ++sync_state;                           // Move to next state
   }                                           //
}                                               //

 

Complete code... (equivalent to video.asm)

// video.c

#include "msp430g2211.h"

extern unsigned pix[14];
static unsigned sync_state;
static unsigned sync_lines;
static unsigned video_state;
static unsigned vid_line;

void video_init(void)
{
   sync_state = 0;
   sync_lines = 1;
   video_state = 0;
   TACCR0 = 1016;
   TACCR1 = 75;
   TACTL   =  0x0210;
   TACCTL1 =  0x0060;
   TACCTL0 |= 0x0010;
   _EINT();
}

void active_video(void)
{
   register unsigned b, n, d;

#if 1
   //__asm(" mov       &TAR, R12");        
   __asm(" mov     &0x0170, R12");
   __asm(" cmp     #144, R12");
   __asm(" jlo     $ - 8");
   __asm(" and     #7, R12");
   __asm(" rla     R12");
   __asm(" add     R12, PC");
   __asm(" nop");
   __asm(" nop");
   __asm(" nop");
   __asm(" nop");
   __asm(" nop");
   __asm(" nop");
   __asm(" nop");
#else
   while(TAR < 160);
#endif

   b = pix[vid_line++ >> 4];
   n = 16;
   do {
       if(b & 0x8000) P1OUT |= 0x80; else { P1OUT &= ~0x80; __asm(" nop"); __asm(" nop"); }
       b <<= 1;
       d = 10; while(--d);
       --n;
   } while(n);
   P1OUT &= ~0x80;
}

#pragma vector = TIMERA0_VECTOR
__interrupt void video_isr(void)                //  Video ISR
{                                               //
   if(--sync_lines) {                          // Decrement the sync line count, if not zero yet then
       if(video_state) active_video();         //  generate video if video_state is non-zero
   } else {                                    // Sync line count is now zero, so update the sync timing
       switch(sync_state) {                    //
           default:                            // Restart state machine
               sync_state = 0;                 //
           case 0: case 2: case 8: case 10:    // Equalizing pulses - 6 half lines
               TACCR1 = 50; TACCR0 = 508;  sync_lines = 6; break;
           case 1: case 9:                     // Vertical sync - 6 half lines
               TACCR1 = 508 - 75;          sync_lines = 6; break;
           case 3: case 12:                    // VBI - 11 lines
               TACCR1 = 75; TACCR0 = 1016; sync_lines = 11; break;
           case 4: case 6: case 13: case 15:   // 9 blank lines 
                                           sync_lines = 9;   video_state = 0; break;
           case 5: case 14:                    // Active video - 224 lines
                                           sync_lines = 224; video_state = 1; vid_line = 0; active_video(); break;
           case 7: case 11:                    // Half line - 1 half line
               TACCR1 = 75; TACCR0 = 508;  sync_lines = 1; break;
       }                                       //
       ++sync_state;                           // Move to next state
   }                                           //
}                                               //

Share this post


Link to post
Share on other sites

Any ideas on how to connect the launchpad s upto a monitor, I did try this over the weekend and got so far but looks like i didn't have any sync?

 

Matt

Share this post


Link to post
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.

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.

Sign in to follow this  

×
×
  • Create New...