mirror of
https://github.com/StepanovPlaton/C3DGraphicEngine.git
synced 2026-04-06 05:40:44 +04:00
Add TV output
This commit is contained in:
517
src/platforms/micro/lib/TVout/video_gen.cpp
Normal file
517
src/platforms/micro/lib/TVout/video_gen.cpp
Normal file
@@ -0,0 +1,517 @@
|
||||
/*
|
||||
Copyright (c) 2010 Myles Metzer
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "video_gen.h"
|
||||
#include "spec/asm_macros.h"
|
||||
|
||||
//#define REMOVE6C
|
||||
//#define REMOVE5C
|
||||
//#define REMOVE4C
|
||||
//#define REMOVE3C
|
||||
|
||||
int renderLine;
|
||||
TVout_vid display;
|
||||
void (*render_line)();
|
||||
void (*line_handler)();
|
||||
void (*hbi_hook)() = ∅
|
||||
void (*vbi_hook)() = ∅
|
||||
|
||||
// sound properties
|
||||
volatile long remainingToneVsyncs;
|
||||
|
||||
void empty() {
|
||||
|
||||
}
|
||||
// start timer
|
||||
void render_setup(uint8_t mode, uint8_t x, uint8_t y, uint8_t *scrnptr) {
|
||||
display.screen = scrnptr;
|
||||
display.hres = x;
|
||||
display.vres = y;
|
||||
display.frames = 0;
|
||||
|
||||
if (mode) {
|
||||
display.vscale_const = _PAL_LINE_DISPLAY / display.vres - 1;
|
||||
} else {
|
||||
display.vscale_const = _NTSC_LINE_DISPLAY / display.vres - 1;
|
||||
}
|
||||
display.vscale = display.vscale_const;
|
||||
|
||||
// selects the widest render method that fits in 46us
|
||||
// as of 9/16/10 rendermode 3 will not work for resolutions lower than
|
||||
// 192(display.hres lower than 24)
|
||||
unsigned char rmethod = (_TIME_ACTIVE*_CYCLES_PER_US) / (display.hres * 8);
|
||||
switch(rmethod) {
|
||||
case 6:
|
||||
render_line = &render_line6c;
|
||||
break;
|
||||
case 5:
|
||||
render_line = &render_line5c;
|
||||
break;
|
||||
case 4:
|
||||
render_line = &render_line4c;
|
||||
break;
|
||||
case 3:
|
||||
render_line = &render_line3c;
|
||||
break;
|
||||
default:
|
||||
if (rmethod > 6) {
|
||||
render_line = &render_line6c;
|
||||
} else {
|
||||
render_line = &render_line3c;
|
||||
}
|
||||
}
|
||||
|
||||
DDR_VID |= _BV(VID_PIN);
|
||||
DDR_SYNC |= _BV(SYNC_PIN);
|
||||
PORT_VID &= ~_BV(VID_PIN);
|
||||
PORT_SYNC |= _BV(SYNC_PIN);
|
||||
// for tone generation
|
||||
DDR_SND |= _BV(SND_PIN);
|
||||
|
||||
// inverted fast pwm mode on timer 1
|
||||
TCCR1A = _BV(COM1A1) | _BV(COM1A0) | _BV(WGM11);
|
||||
TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10);
|
||||
|
||||
if (mode) {
|
||||
display.start_render = _PAL_LINE_MID - ((display.vres * (display.vscale_const + 1)) / 2);
|
||||
display.output_delay = _PAL_CYCLES_OUTPUT_START;
|
||||
display.vsync_end = _PAL_LINE_STOP_VSYNC;
|
||||
display.lines_frame = _PAL_LINE_FRAME;
|
||||
ICR1 = _PAL_CYCLES_SCANLINE;
|
||||
OCR1A = _CYCLES_HORZ_SYNC;
|
||||
} else {
|
||||
display.start_render = _NTSC_LINE_MID - ((display.vres * (display.vscale_const+1))/2) + 8;
|
||||
display.output_delay = _NTSC_CYCLES_OUTPUT_START;
|
||||
display.vsync_end = _NTSC_LINE_STOP_VSYNC;
|
||||
display.lines_frame = _NTSC_LINE_FRAME;
|
||||
ICR1 = _NTSC_CYCLES_SCANLINE;
|
||||
OCR1A = _CYCLES_HORZ_SYNC;
|
||||
}
|
||||
display.scanLine = display.lines_frame + 1;
|
||||
line_handler = &vsync_line;
|
||||
TIMSK1 = _BV(TOIE1);
|
||||
sei();
|
||||
}
|
||||
|
||||
// render a line
|
||||
ISR(TIMER1_OVF_vect) {
|
||||
hbi_hook();
|
||||
line_handler();
|
||||
}
|
||||
|
||||
// вызываем синхронизацию или прорисовку строки
|
||||
void blank_line() {
|
||||
if (display.scanLine == display.start_render) {
|
||||
renderLine = 0;
|
||||
display.vscale = display.vscale_const;
|
||||
line_handler = &active_line;
|
||||
} else if (display.scanLine == display.lines_frame) {
|
||||
line_handler = &vsync_line;
|
||||
vbi_hook();
|
||||
}
|
||||
display.scanLine++;
|
||||
}
|
||||
|
||||
|
||||
void active_line() {
|
||||
wait_until(display.output_delay);
|
||||
render_line();
|
||||
if (!display.vscale) {
|
||||
display.vscale = display.vscale_const;
|
||||
renderLine += display.hres;
|
||||
} else {
|
||||
display.vscale--;
|
||||
}
|
||||
if ((display.scanLine + 1) == (int)(display.start_render + (display.vres * (display.vscale_const + 1)))) {
|
||||
line_handler = &blank_line;
|
||||
}
|
||||
display.scanLine++;
|
||||
}
|
||||
|
||||
// генерируем синхроимпульс
|
||||
void vsync_line() {
|
||||
if (display.scanLine >= display.lines_frame) {
|
||||
OCR1A = _CYCLES_VIRT_SYNC;
|
||||
display.scanLine = 0;
|
||||
display.frames++;
|
||||
if (remainingToneVsyncs != 0) {
|
||||
if (remainingToneVsyncs > 0) {
|
||||
remainingToneVsyncs--;
|
||||
}
|
||||
} else {
|
||||
// stop the tone
|
||||
TCCR2B = 0;
|
||||
PORTB &= ~(_BV(SND_PIN));
|
||||
}
|
||||
} else if (display.scanLine == display.vsync_end) {
|
||||
OCR1A = _CYCLES_HORZ_SYNC;
|
||||
line_handler = &blank_line;
|
||||
}
|
||||
display.scanLine++;
|
||||
}
|
||||
|
||||
|
||||
static void inline wait_until(uint8_t time) {
|
||||
__asm__ __volatile__ (
|
||||
"subi %[time], 10\n"
|
||||
"sub %[time], %[tcnt1l]\n\t"
|
||||
"100:\n\t"
|
||||
"subi %[time], 3\n\t"
|
||||
"brcc 100b\n\t"
|
||||
"subi %[time], 0-3\n\t"
|
||||
"breq 101f\n\t"
|
||||
"dec %[time]\n\t"
|
||||
"breq 102f\n\t"
|
||||
"rjmp 102f\n"
|
||||
"101:\n\t"
|
||||
"nop\n"
|
||||
"102:\n"
|
||||
:
|
||||
: [time] "a" (time),
|
||||
[tcnt1l] "a" (TCNT1L)
|
||||
);
|
||||
}
|
||||
|
||||
void render_line6c() {
|
||||
#ifndef REMOVE6C
|
||||
__asm__ __volatile__ (
|
||||
"ADD r26,r28\n\t"
|
||||
"ADC r27,r29\n\t"
|
||||
// save PORTB
|
||||
"svprt %[port]\n\t"
|
||||
"rjmp enter6\n"
|
||||
"loop6:\n\t"
|
||||
// 8
|
||||
"bst __tmp_reg__,0\n\t"
|
||||
"o1bs %[port]\n"
|
||||
"enter6:\n\t"
|
||||
// 1
|
||||
"LD __tmp_reg__,X+\n\t"
|
||||
"delay1\n\t"
|
||||
"bst __tmp_reg__,7\n\t"
|
||||
"o1bs %[port]\n\t"
|
||||
// 2
|
||||
"delay3\n\t"
|
||||
"bst __tmp_reg__,6\n\t"
|
||||
"o1bs %[port]\n\t"
|
||||
// 3
|
||||
"delay3\n\t"
|
||||
"bst __tmp_reg__,5\n\t"
|
||||
"o1bs %[port]\n\t"
|
||||
// 4
|
||||
"delay3\n\t"
|
||||
"bst __tmp_reg__,4\n\t"
|
||||
"o1bs %[port]\n\t"
|
||||
// 5
|
||||
"delay3\n\t"
|
||||
"bst __tmp_reg__,3\n\t"
|
||||
"o1bs %[port]\n\t"
|
||||
// 6
|
||||
"delay3\n\t"
|
||||
"bst __tmp_reg__,2\n\t"
|
||||
"o1bs %[port]\n\t"
|
||||
// 7
|
||||
"delay3\n\t"
|
||||
"bst __tmp_reg__,1\n\t"
|
||||
"o1bs %[port]\n\t"
|
||||
"dec %[hres]\n\t"
|
||||
// go too loopsix
|
||||
"brne loop6\n\t"
|
||||
"delay2\n\t"
|
||||
// 8
|
||||
"bst __tmp_reg__,0\n\t"
|
||||
"o1bs %[port]\n"
|
||||
"svprt %[port]\n\t"
|
||||
BST_HWS
|
||||
"o1bs %[port]\n\t"
|
||||
:
|
||||
: [port] "i" (_SFR_IO_ADDR(PORT_VID)),
|
||||
"x" (display.screen),
|
||||
"y" (renderLine),
|
||||
[hres] "d" (display.hres)
|
||||
: "r16" // try to remove this clobber later...
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
void render_line5c() {
|
||||
#ifndef REMOVE5C
|
||||
__asm__ __volatile__ (
|
||||
"ADD r26,r28\n\t"
|
||||
"ADC r27,r29\n\t"
|
||||
// save PORTB
|
||||
"svprt %[port]\n\t"
|
||||
"rjmp enter5\n"
|
||||
"loop5:\n\t"
|
||||
// 8
|
||||
"bst __tmp_reg__,0\n\t"
|
||||
"o1bs %[port]\n"
|
||||
"enter5:\n\t"
|
||||
// 1
|
||||
"LD __tmp_reg__,X+\n\t"
|
||||
"bst __tmp_reg__,7\n\t"
|
||||
"o1bs %[port]\n\t"
|
||||
// 2
|
||||
"delay2\n\t"
|
||||
"bst __tmp_reg__,6\n\t"
|
||||
"o1bs %[port]\n\t"
|
||||
// 3
|
||||
"delay2\n\t"
|
||||
"bst __tmp_reg__,5\n\t"
|
||||
"o1bs %[port]\n\t"
|
||||
// 4
|
||||
"delay2\n\t"
|
||||
"bst __tmp_reg__,4\n\t"
|
||||
"o1bs %[port]\n\t"
|
||||
// 5
|
||||
"delay2\n\t"
|
||||
"bst __tmp_reg__,3\n\t"
|
||||
"o1bs %[port]\n\t"
|
||||
// 6
|
||||
"delay2\n\t"
|
||||
"bst __tmp_reg__,2\n\t"
|
||||
"o1bs %[port]\n\t"
|
||||
// 7
|
||||
"delay1\n\t"
|
||||
"dec %[hres]\n\t"
|
||||
"bst __tmp_reg__,1\n\t"
|
||||
"o1bs %[port]\n\t"
|
||||
// go too loop5
|
||||
"brne loop5\n\t"
|
||||
"delay1\n\t"
|
||||
// 8
|
||||
"bst __tmp_reg__,0\n\t"
|
||||
"o1bs %[port]\n"
|
||||
"svprt %[port]\n\t"
|
||||
BST_HWS
|
||||
"o1bs %[port]\n\t"
|
||||
:
|
||||
: [port] "i" (_SFR_IO_ADDR(PORT_VID)),
|
||||
"x" (display.screen),
|
||||
"y" (renderLine),
|
||||
[hres] "d" (display.hres)
|
||||
: "r16" // try to remove this clobber later...
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
void render_line4c() {
|
||||
#ifndef REMOVE4C
|
||||
__asm__ __volatile__ (
|
||||
"ADD r26,r28\n\t"
|
||||
"ADC r27,r29\n\t"
|
||||
"rjmp enter4\n"
|
||||
"loop4:\n\t"
|
||||
// 8
|
||||
"lsl __tmp_reg__\n\t"
|
||||
"out %[port],__tmp_reg__\n\t"
|
||||
"enter4:\n\t"
|
||||
// 1
|
||||
"LD __tmp_reg__,X+\n\t"
|
||||
"delay1\n\t"
|
||||
"out %[port],__tmp_reg__\n\t"
|
||||
// 2
|
||||
"delay2\n\t"
|
||||
"lsl __tmp_reg__\n\t"
|
||||
"out %[port],__tmp_reg__\n\t"
|
||||
// 3
|
||||
"delay2\n\t"
|
||||
"lsl __tmp_reg__\n\t"
|
||||
"out %[port],__tmp_reg__\n\t"
|
||||
// 4
|
||||
"delay2\n\t"
|
||||
"lsl __tmp_reg__\n\t"
|
||||
"out %[port],__tmp_reg__\n\t"
|
||||
"delay2\n\t"
|
||||
"lsl __tmp_reg__\n\t"
|
||||
"out %[port],__tmp_reg__\n\t"
|
||||
// 6
|
||||
"delay2\n\t"
|
||||
"lsl __tmp_reg__\n\t"
|
||||
"out %[port],__tmp_reg__\n\t"
|
||||
// 7
|
||||
"delay1\n\t"
|
||||
"lsl __tmp_reg__\n\t"
|
||||
"dec %[hres]\n\t"
|
||||
"out %[port],__tmp_reg__\n\t"
|
||||
// go too loop4
|
||||
"brne loop4\n\t"
|
||||
// 8
|
||||
"delay1\n\t"
|
||||
"lsl __tmp_reg__\n\t"
|
||||
"out %[port],__tmp_reg__\n\t"
|
||||
"delay3\n\t"
|
||||
"cbi %[port],7\n\t"
|
||||
:
|
||||
: [port] "i" (_SFR_IO_ADDR(PORT_VID)),
|
||||
"x" (display.screen),
|
||||
"y" (renderLine),
|
||||
[hres] "d" (display.hres)
|
||||
: "r16" // try to remove this clobber later...
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
// only 16mhz right now!!!
|
||||
void render_line3c() {
|
||||
#ifndef REMOVE3C
|
||||
__asm__ __volatile__ (
|
||||
".macro byteshift\n\t"
|
||||
"LD __tmp_reg__,X+\n\t"
|
||||
// 0
|
||||
"out %[port],__tmp_reg__\n\t"
|
||||
"nop\n\t"
|
||||
"lsl __tmp_reg__\n\t"
|
||||
// 1
|
||||
"out %[port],__tmp_reg__\n\t"
|
||||
"nop\n\t"
|
||||
"lsl __tmp_reg__\n\t"
|
||||
// 2
|
||||
"out %[port],__tmp_reg__\n\t"
|
||||
"nop\n\t"
|
||||
"lsl __tmp_reg__\n\t"
|
||||
// 3
|
||||
"out %[port],__tmp_reg__\n\t"
|
||||
"nop\n\t"
|
||||
"lsl __tmp_reg__\n\t"
|
||||
// 4
|
||||
"out %[port],__tmp_reg__\n\t"
|
||||
"nop\n\t"
|
||||
"lsl __tmp_reg__\n\t"
|
||||
// 5
|
||||
"out %[port],__tmp_reg__\n\t"
|
||||
"nop\n\t"
|
||||
"lsl __tmp_reg__\n\t"
|
||||
// 6
|
||||
"out %[port],__tmp_reg__\n\t"
|
||||
"nop\n\t"
|
||||
"lsl __tmp_reg__\n\t"
|
||||
// 7
|
||||
"out %[port],__tmp_reg__\n\t"
|
||||
".endm\n\t"
|
||||
"ADD r26,r28\n\t"
|
||||
"ADC r27,r29\n\t"
|
||||
// 615
|
||||
"cpi %[hres],30\n\t"
|
||||
"breq skip0\n\t"
|
||||
"cpi %[hres],29\n\t"
|
||||
"breq jumpto1\n\t"
|
||||
"cpi %[hres],28\n\t"
|
||||
"breq jumpto2\n\t"
|
||||
"cpi %[hres],27\n\t"
|
||||
"breq jumpto3\n\t"
|
||||
"cpi %[hres],26\n\t"
|
||||
"breq jumpto4\n\t"
|
||||
"cpi %[hres],25\n\t"
|
||||
"breq jumpto5\n\t"
|
||||
"cpi %[hres],24\n\t"
|
||||
"breq jumpto6\n\t"
|
||||
"jumpto1:\n\t"
|
||||
"rjmp skip1\n\t"
|
||||
"jumpto2:\n\t"
|
||||
"rjmp skip2\n\t"
|
||||
"jumpto3:\n\t"
|
||||
"rjmp skip3\n\t"
|
||||
"jumpto4:\n\t"
|
||||
"rjmp skip4\n\t"
|
||||
"jumpto5:\n\t"
|
||||
"rjmp skip5\n\t"
|
||||
"jumpto6:\n\t"
|
||||
"rjmp skip6\n\t"
|
||||
"skip0:\n\t"
|
||||
//1 643
|
||||
"byteshift\n\t"
|
||||
"skip1:\n\t"
|
||||
// 2
|
||||
"byteshift\n\t"
|
||||
"skip2:\n\t"
|
||||
// 3
|
||||
"byteshift\n\t"
|
||||
"skip3:\n\t"
|
||||
// 4
|
||||
"byteshift\n\t"
|
||||
"skip4:\n\t"
|
||||
// 5
|
||||
"byteshift\n\t"
|
||||
"skip5:\n\t"
|
||||
// 6
|
||||
"byteshift\n\t"
|
||||
"skip6:\n\t"
|
||||
// 7
|
||||
"byteshift\n\t"
|
||||
// 8
|
||||
"byteshift\n\t"
|
||||
// 9
|
||||
"byteshift\n\t"
|
||||
// 10
|
||||
"byteshift\n\t"
|
||||
// 11
|
||||
"byteshift\n\t"
|
||||
// 12
|
||||
"byteshift\n\t"
|
||||
// 13
|
||||
"byteshift\n\t"
|
||||
// 14
|
||||
"byteshift\n\t"
|
||||
// 15
|
||||
"byteshift\n\t"
|
||||
// 16
|
||||
"byteshift\n\t"
|
||||
// 17
|
||||
"byteshift\n\t"
|
||||
// 18
|
||||
"byteshift\n\t"
|
||||
// 19
|
||||
"byteshift\n\t"
|
||||
// 20
|
||||
"byteshift\n\t"
|
||||
// 21
|
||||
"byteshift\n\t"
|
||||
// 22
|
||||
"byteshift\n\t"
|
||||
// 23
|
||||
"byteshift\n\t"
|
||||
// 24
|
||||
"byteshift\n\t"
|
||||
// 25
|
||||
"byteshift\n\t"
|
||||
// 26
|
||||
"byteshift\n\t"
|
||||
// 27
|
||||
"byteshift\n\t"
|
||||
// 28
|
||||
"byteshift\n\t"
|
||||
// 29
|
||||
"byteshift\n\t"
|
||||
// 30
|
||||
"byteshift\n\t"
|
||||
"delay2\n\t"
|
||||
"cbi %[port],7\n\t"
|
||||
:
|
||||
: [port] "i" (_SFR_IO_ADDR(PORT_VID)),
|
||||
"x" (display.screen),
|
||||
"y" (renderLine),
|
||||
[hres] "d" (display.hres)
|
||||
: "r16" // try to remove this clobber later...
|
||||
);
|
||||
#endif
|
||||
}
|
||||
Reference in New Issue
Block a user