And here is the code to run it....
/*
ATTINY13A pinout :
RESET 1 8 VCC
32mhz CLOCK 2 7 GREEN
BLUE 3 6 RED
GND 4 5 VHSYNC
MICRO PORT RES SIGNAL VGA CONN
Pin 5 - PB0 1k VHsync (pin13)
Pin 6 - PB1 47r RED (pin1)
Pin 7 - PB2 47r GREEN (pin2)
Pin 3 - PB4 47r BLUE (pin3)
*/
// Video Status
#define VS_VSYNC_LINE 0
#define VS_BLANK_LINE 1
#define VS_ACTIVE_LINE 2
// scanLine state triggers
#define END_OF_VSYNC 7
#define START_RENDER 42
#define END_RENDER 282
#define END_OF_FRAME 312
// compA/B interrupt trigger points
#define CYCLES_HORZ_SYNC 16 // end point of HSYNC
#define CYCLES_VERT_SYNC 255 // maxed out comp reg for flat line during VSync...
#define CYCLES_RENDER_START 40 // horizontal start point of render
// hardware defines
#define PORT_VID PORTB
#define VSCALE_CONST 5
// RAM - 64 bytes
volatile uint8_t H_RED_data[5], H_GREEN_data[5], H_BLUE_data[5];
volatile uint8_t V_RED_data[5], V_GREEN_data[5], V_BLUE_data[5];
uint8_t vscale = VSCALE_CONST;
int16_t scanLine = -1;
uint8_t V_data_bits;
int8_t renderLine;
volatile uint8_t timer;
int main(void) {
DDRB = 0b00010111; // set up output ports
TCCR0A = _BV(WGM00) | _BV(WGM01) | _BV (COM0A1) | _BV(COM0A0); // fast PWM H&VSync pulses on OC0A (pin5)
TCCR0B = _BV(CS01) ; // fast PWM, top at 0xFF, div8 clock
OCR0A = CYCLES_HORZ_SYNC; // 4us(16 clicks) required for Hsync
OCR0B = CYCLES_RENDER_START; // ~12.5us(40 clicks) from line start to render start
TIMSK0 |= _BV(TOIE0); // timer 0 compare B interrupt enable
sei();
// set up colour arrays
H_GREEN_data[0] = 0b00000011;
H_BLUE_data[0] = 0b00000011;
for (uint8_t x=0; x<5; x++) {
V_BLUE_data[x] = 0b11111111;
V_GREEN_data[x] = 0b11111111;
H_RED_data[x] = 0b00011000;
V_RED_data[x] = 0b00011000;
}
while(1) { // this is the main loop
if (timer == 2) {
// timed code here....
shiftFwd(&H_GREEN_data[0]);
shiftFwd(&H_BLUE_data[0]);
timer=0;
}
} // while
}
void shiftFwd(volatile uint8_t *COL) {
asm volatile(
"ld r16,x+\n\t"
"ld r17,x+\n\t"
"ld r18,x+\n\t"
"ld r19,x+\n\t"
"ld r20,x\n\t"
"bst r20,0\n\t"
"lsr r16\n\t"
"ror r17\n\t"
"ror r18\n\t"
"ror r19\n\t"
"ror r20\n\t"
"bld r16,7\n\t"
"clc\n\t"
"st x, r20\n\t"
"st -x, r19\n\t"
"st -x, r18\n\t"
"st -x, r17\n\t"
"st -x, r16\n\t"
::
"x" (COL) // x (r27:r26)
:
"r16", "r17", "r18", "r19", "r20", "r21" // clobbers
); // end of asm
}
void shiftRev(volatile uint8_t *COL) {
asm volatile(
"ld r16,x+\n\t"
"ld r17,x+\n\t"
"ld r18,x+\n\t"
"ld r19,x+\n\t"
"ld r20,x\n\t"
"bst r16,7\n\t"
"lsl r20\n\t"
"rol r19\n\t"
"rol r18\n\t"
"rol r17\n\t"
"rol r16\n\t"
"bld r20,0\n\t"
"clc\n\t"
"st x, r20\n\t"
"st -x, r19\n\t"
"st -x, r18\n\t"
"st -x, r17\n\t"
"st -x, r16\n\t"
::
"x" (COL) // x (r27:r26)
:
"r16", "r17", "r18", "r19", "r20", "r21" // clobbers
); // end of asm
}
void setVbits(void) {
uint8_t yByte = renderLine>>3;
uint8_t yBit = 7-(renderLine&7);
V_data_bits = (V_RED_data[yByte] >> yBit) & 1;
V_data_bits |= ((V_GREEN_data[yByte] >> yBit) & 1) << 1;
V_data_bits |= ((V_BLUE_data[yByte] >> yBit) & 1) << 2;
}
ISR(TIM0_OVF_vect){
if (scanLine == START_RENDER-1) { vscale = VSCALE_CONST; renderLine = 0; setVbits(); TIMSK0 |= _BV(OCIE0B); }
else if (scanLine > START_RENDER-1 && scanLine < END_RENDER-1) {
if (!vscale) {
vscale = VSCALE_CONST;
renderLine++;
setVbits();
}
else { vscale--; }
}
else if (scanLine == END_RENDER) { TIMSK0 &= ~_BV(OCIE0B); }
else if (scanLine == END_OF_VSYNC) { OCR0A = CYCLES_HORZ_SYNC; }
else if (scanLine == END_OF_FRAME) { OCR0A = CYCLES_VERT_SYNC; scanLine =-1; timer++; } // do audio sample here
scanLine++;
}
ISR(TIM0_COMPB_vect) {
if ( scanLine > START_RENDER) {
render();
}
}
void render(void) {
asm volatile(
// line start delay
"ldi r19,22\n\t" // set delay amount
"S_delay_%=:\n\t"
"dec r19\n\t" // decrement delay
"brne S_delay_%=\n\t"
// init
"in r16,%[port]\n\t" // get contents of port and save in r16
"ldi r17,5\n\t" // 5 bytes per line r17=byte count
// get bytes for RG&B
"byte_loop_%=:\n\t"
"ld r23,x+\n\t" // r18 = red byte
"ld r24,y+\n\t" // rGG = green byte
"ld r25,z+\n\t" // rBB = blue byte
"ldi r18, 8\n\t " // r20 = bit count
// deal with bits, first RED
"bit_loop_%=:\n\t"
"bst r23, 7\n\t" // grab Tbit from red byte
"brtc red_not_set_%=\n\t" // if clear then clear !
"bst %[vData], 0\n\t" // grab red bit (0) from V_data
"bld r16, 1\n\t" // store T (v_data bit) into r16 red bit
"rjmp green_%=\n\t"
// NO RED
"red_not_set_%=:\n\t"
"andi r16, 0b11111101\n\t" // unset red
"nop\n\t nop\n\t nop\n\t"
// deal with GREEN
"green_%=:\n\t"
"bst r24, 7\n\t" // grab Tbit from green byte
"brtc green_not_set_%=\n\t" // if clear then clear !
"bst %[vData], 1\n\t" // grab green bit (1) from V_data
"bld r16, 2\n\t" // store T (v_data bit) into r16 green bit
"rjmp blue_%=\n\t"
// NO GREEN
"green_not_set_%=:\n\t"
"andi r16, 0b11111011\n\t" // unset green
"nop\n\t nop\n\t nop\n\t"
// deal with BLUE
"blue_%=:\n\t"
"bst r25, 7\n\t" // grab Tbit from green byte
"brtc blue_not_set_%=\n\t" // if clear then clear !
"bst %[vData], 2\n\t" // grab blue bit (2) from V_data
"bld r16, 4\n\t" // store T (v_data bit) into r16 green bit
"rjmp next_bit_%=\n\t"
// NO BLUE
"blue_not_set_%=:\n\t"
"andi r16, 0b11101111\n\t" // unset blue
"nop\n\t nop\n\t nop\n\t"
// output RG&B to port
"next_bit_%=:\n\t"
"out %[port], r16\n\t" // send result to port
// delay before next bit
"ldi r19,2\n\t" // set delay amount
"B_delay_%=:\n\t"
"dec r19\n\t" // decrement delay
"brne B_delay_%=\n\t"
// roll all to get next bits
"rol r23\n\t" // roll red byte to get next bit
"rol r24\n\t" // roll green byte to get next bit
"rol r25\n\t" // roll blue byte to get next bit
"dec r18\n\t" // dec bit count
"brne bit_loop_%=\n\t" // go get next bit
// go get next byte
"dec r17\n\t" // dec byte count
"brne byte_loop_%=\n\t" // go get next byte
// delay before clearing outputs at line end !
"nop\n\t nop\n\t nop\n\t nop\n\t"
"nop\n\t nop\n\t nop\n\t nop\n\t"
"andi r16, 0b11101001\n\t" // clear output for end of line
"out %[port], r16\n\t" // output clear
::
[port] "i" (_SFR_IO_ADDR(PORT_VID)), // output port
[vData] "r" (V_data_bits), // vertical data bits 0b00000BGR for vertical line enable
"x" (&H_RED_data[0]), // x (r27:r26)
"y" (&H_GREEN_data[0]), // y (r29:r28)
"z" (&H_BLUE_data[0]) // z (r31:r30)
:
"r16", "r17", "r18", "r19", "r23", "r24", "r25"// clobbers
); // end of asm
}