summaryrefslogtreecommitdiff
path: root/demontsc/lib/TVout/video_gen.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'demontsc/lib/TVout/video_gen.cpp')
-rwxr-xr-xdemontsc/lib/TVout/video_gen.cpp470
1 files changed, 470 insertions, 0 deletions
diff --git a/demontsc/lib/TVout/video_gen.cpp b/demontsc/lib/TVout/video_gen.cpp
new file mode 100755
index 0000000..8e21b09
--- /dev/null
+++ b/demontsc/lib/TVout/video_gen.cpp
@@ -0,0 +1,470 @@
+/*
+ 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 <avr/interrupt.h>
+#include <avr/io.h>
+
+#include "video_gen.h"
+#include "spec/video_properties.h"
+#include "spec/asm_macros.h"
+#include "spec/hardware_setup.h"
+
+//#define REMOVE6C
+//#define REMOVE5C
+//#define REMOVE4C
+//#define REMOVE3C
+
+int renderLine;
+TVout_vid display;
+void (*render_line)(); //remove me
+void (*line_handler)(); //remove me
+void (*hbi_hook)() = &empty;
+void (*vbi_hook)() = &empty;
+
+// sound properties
+volatile long remainingToneVsyncs;
+
+void empty() {}
+
+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);
+ DDR_SND |= _BV(SND_PIN); // for tone generation.
+
+ // 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
+ {
+ TCCR2B = 0; //stop the tone
+ 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"
+ "bst __tmp_reg__,0\n\t" //8
+ "o1bs %[port]\n"
+ "enter6:\n\t"
+ "LD __tmp_reg__,X+\n\t" //1
+ "delay1\n\t"
+ "bst __tmp_reg__,7\n\t"
+ "o1bs %[port]\n\t"
+ "delay3\n\t" //2
+ "bst __tmp_reg__,6\n\t"
+ "o1bs %[port]\n\t"
+ "delay3\n\t" //3
+ "bst __tmp_reg__,5\n\t"
+ "o1bs %[port]\n\t"
+ "delay3\n\t" //4
+ "bst __tmp_reg__,4\n\t"
+ "o1bs %[port]\n\t"
+ "delay3\n\t" //5
+ "bst __tmp_reg__,3\n\t"
+ "o1bs %[port]\n\t"
+ "delay3\n\t" //6
+ "bst __tmp_reg__,2\n\t"
+ "o1bs %[port]\n\t"
+ "delay3\n\t" //7
+ "bst __tmp_reg__,1\n\t"
+ "o1bs %[port]\n\t"
+ "dec %[hres]\n\t"
+ "brne loop6\n\t" //go too loopsix
+ "delay2\n\t"
+ "bst __tmp_reg__,0\n\t" //8
+ "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"
+ "bst __tmp_reg__,0\n\t" //8
+ "o1bs %[port]\n"
+ "enter5:\n\t"
+ "LD __tmp_reg__,X+\n\t" //1
+ "bst __tmp_reg__,7\n\t"
+ "o1bs %[port]\n\t"
+ "delay2\n\t" //2
+ "bst __tmp_reg__,6\n\t"
+ "o1bs %[port]\n\t"
+ "delay2\n\t" //3
+ "bst __tmp_reg__,5\n\t"
+ "o1bs %[port]\n\t"
+ "delay2\n\t" //4
+ "bst __tmp_reg__,4\n\t"
+ "o1bs %[port]\n\t"
+ "delay2\n\t" //5
+ "bst __tmp_reg__,3\n\t"
+ "o1bs %[port]\n\t"
+ "delay2\n\t" //6
+ "bst __tmp_reg__,2\n\t"
+ "o1bs %[port]\n\t"
+ "delay1\n\t" //7
+ "dec %[hres]\n\t"
+ "bst __tmp_reg__,1\n\t"
+ "o1bs %[port]\n\t"
+ "brne loop5\n\t" //go too loop5
+ "delay1\n\t"
+ "bst __tmp_reg__,0\n\t" //8
+ "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"
+ "lsl __tmp_reg__\n\t" //8
+ "out %[port],__tmp_reg__\n\t"
+ "enter4:\n\t"
+ "LD __tmp_reg__,X+\n\t" //1
+ "delay1\n\t"
+ "out %[port],__tmp_reg__\n\t"
+ "delay2\n\t" //2
+ "lsl __tmp_reg__\n\t"
+ "out %[port],__tmp_reg__\n\t"
+ "delay2\n\t" //3
+ "lsl __tmp_reg__\n\t"
+ "out %[port],__tmp_reg__\n\t"
+ "delay2\n\t" //4
+ "lsl __tmp_reg__\n\t"
+ "out %[port],__tmp_reg__\n\t"
+ "delay2\n\t" //5
+ "lsl __tmp_reg__\n\t"
+ "out %[port],__tmp_reg__\n\t"
+ "delay2\n\t" //6
+ "lsl __tmp_reg__\n\t"
+ "out %[port],__tmp_reg__\n\t"
+ "delay1\n\t" //7
+ "lsl __tmp_reg__\n\t"
+ "dec %[hres]\n\t"
+ "out %[port],__tmp_reg__\n\t"
+ "brne loop4\n\t" //go too loop4
+ "delay1\n\t" //8
+ "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"
+ "out %[port],__tmp_reg__\n\t" //0
+ "nop\n\t"
+ "lsl __tmp_reg__\n\t"
+ "out %[port],__tmp_reg__\n\t" //1
+ "nop\n\t"
+ "lsl __tmp_reg__\n\t"
+ "out %[port],__tmp_reg__\n\t" //2
+ "nop\n\t"
+ "lsl __tmp_reg__\n\t"
+ "out %[port],__tmp_reg__\n\t" //3
+ "nop\n\t"
+ "lsl __tmp_reg__\n\t"
+ "out %[port],__tmp_reg__\n\t" //4
+ "nop\n\t"
+ "lsl __tmp_reg__\n\t"
+ "out %[port],__tmp_reg__\n\t" //5
+ "nop\n\t"
+ "lsl __tmp_reg__\n\t"
+ "out %[port],__tmp_reg__\n\t" //6
+ "nop\n\t"
+ "lsl __tmp_reg__\n\t"
+ "out %[port],__tmp_reg__\n\t" //7
+ ".endm\n\t"
+
+ "ADD r26,r28\n\t"
+ "ADC r27,r29\n\t"
+
+ "cpi %[hres],30\n\t" //615
+ "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"
+ "byteshift\n\t" //1 \\643
+ "skip1:\n\t"
+ "byteshift\n\t" //2
+ "skip2:\n\t"
+ "byteshift\n\t" //3
+ "skip3:\n\t"
+ "byteshift\n\t" //4
+ "skip4:\n\t"
+ "byteshift\n\t" //5
+ "skip5:\n\t"
+ "byteshift\n\t" //6
+ "skip6:\n\t"
+ "byteshift\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
+
+ "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
+}