15Khz (240p) Signal Generator
April 3, 2019This is the firmware used in the Sweet15 device which you can read about here.
The current code was written in BASCOM-AVR, and I used the built in debugger along with a Saleae Logic analyzer to compare the real world output with that of a Super Nintendo. I debugged the tar out of this thing to get the timings hopefully near perfect. You can program the BASCOM code easily onto a ATTINY2313 with the cheap, yet powerful, TL866II Plus.
Here is the code for NTSC, which is a modified version of a great PAL example, a large inspiration for this project, which I found here. Major credits go to this individual (‘Dim’).
'The MIT License (MIT)
'Copyright (c) 2019 Chipnetics Computing (www.chipnetics.com)
'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.
$regfile = "attiny2313.DAT"
$hwstack = 40
$swstack = 20
$framesize = 24
$crystal = 20000000
Config Timer0 = Timer , Prescale = 8
On Timer0 Frame2
Enable Interrupts
Dim Linecount As Word
Dim Drawing As Word
Dim Count As Byte
Dim X As Bit
Const Red = 12
Const Green = 10
Const Blue = 9
Const Yellow = 14
Const Magenta = 13
Const Lblue = 11
Const White = 15
Const Black = 8
'DDRx - Port X Data Direction Register
'Writing 1 in the pin location makes output pin. 0 makes input pin
'Data Direction Register. 0 = Input, 1 = Output
Ddrb = &B00000100 ' B2 is the LED
Ddrd = &B11111111
'PINx - Port X Input Pins Register (READING)
'To READ the values on the pins of PortX you read the values that are on the pin
'register.
'PORTx - Port X Data Register. (OUTPUTTING)
'Stores logic values currently being OUTPUTTED on the physical pinds of PORTx
Portb = &B00000101
Selector Alias Pinb.0
Led Alias Portb.2
Video Alias Portd
Drawing = 0
Linecount = 0
Count = 0
Enable Timer0
'--------------------------------------------------------------------------------
Bardraw_screen:
While Pinb.0 = 0
Wend
Do
rcall bardraw
If Pinb.0 = 0 Then Exit Do
Idle
Loop
'--------------------------------------------------------------------------------
Red_screen:
While Pinb.0 = 0
Wend
Do
Video = Red
If Pinb.0 = 0 Then Exit Do
Idle
Loop
'--------------------------------------------------------------------------------
Green_screen:
While Pinb.0 = 0
Wend
Do
Video = Green
If Pinb.0 = 0 Then Exit Do
Idle
Loop
'--------------------------------------------------------------------------------
Blue_screen:
While Pinb.0 = 0
Wend
Do
Video = Blue
If Pinb.0 = 0 Then Exit Do
Idle
Loop
'--------------------------------------------------------------------------------
Yellow_screen:
While Pinb.0 = 0
Wend
Do
Video = Yellow
If Pinb.0 = 0 Then Exit Do
Idle
Loop
'--------------------------------------------------------------------------------
Magenta_screen:
While Pinb.0 = 0
Wend
Do
Video = Magenta
If Pinb.0 = 0 Then Exit Do
Idle
Loop
'--------------------------------------------------------------------------------
Lblue_screen:
While Pinb.0 = 0
Wend
Do
Video = Lblue
If Pinb.0 = 0 Then Exit Do
Idle
Loop
'--------------------------------------------------------------------------------
White_screen:
While Pinb.0 = 0
Wend
Do
Video = White
If Pinb.0 = 0 Then Exit Do
Idle
Loop
'--------------------------------------------------------------------------------
Black_screen:
While Pinb.0 = 0
Wend
Do
Video = Black
If Pinb.0 = 0 Then Exit Do
Idle
Loop
'--------------------------------------------------------------------------------
220blue_screen:
While Pinb.0 = 0
Wend
Do
If Linecount < 241 Then
Video = Blue
Else
Video = Red
End If
If Pinb.0 = 0 Then Exit Do
Idle
Loop
'--------------------------------------------------------------------------------
Crosshatch:
While Pinb.0 = 0
Wend
Do
If Linecount = 40 Or Linecount = 100 Or Linecount = 160 Or Linecount = 220 Or Linecount = 280 Then
Video = White
Else
rcall line_draw
End If
If Pinb.0 = 0 Then Exit Do
Idle
Loop
'--------------------------------------------------------------------------------
' No more screens to jump through.... start at beginning
'--------------------------------------------------------------------------------
Goto Bardraw_screen
'--------------------------------------------------------------------------------
' Helper Drawing Functions
'--------------------------------------------------------------------------------
Line_draw:
ldi R17, $06
Del3:
dec R17
brne del3
rcall crosshatch0
Video = White
Nop
Video = Black
rcall crosshatch0
Video = White
Nop
Video = Black
rcall crosshatch0
Video = White
Nop
Video = Black
rcall crosshatch0
Video = White
Nop
Video = Black
rcall crosshatch0
Video = White
Nop
Video = Black
Return
'--------------------------------------------------------------------------------
Bardraw:
Video = White
RCALL Delay_time
Video = Yellow
RCALL Delay_time
Video = Lblue
RCALL Delay_time
Video = Green
RCALL Delay_time
Video = Magenta
RCALL Delay_time
Video = Red
RCALL Delay_time
Video = Black
RCALL Delay_time
Video = Blue
Return
'--------------------------------------------------------------------------------
Delay16:
ldi R17, $4d
Wgloop0:
dec R17
brne wgloop0
nop
nop
ret
'--------------------------------------------------------------------------------
Delay_time:
ldi R17, $25
Bars1:
dec R17
brne bars1
ret
'--------------------------------------------------------------------------------
Crosshatch0:
ldi R17, $35
Crosshatch1:
dec R17
brne crosshatch1
nop
ret
'--------------------------------------------------------------------------------
' ISR for Timer0. When trigger Csync and RGB go Low.
'--------------------------------------------------------------------------------
Frame2:
Video = 0
'Low Duration Loop
Incr Linecount
ldi R17, $15 '
Frame_delay:
dec R17
brne frame_delay
If Linecount >= 262 Then ' Blanking period
Timer0 = &H00
ldi R17, $FF
Low_blank_delay_1:
dec R17
brne low_blank_delay_1
Timer0 = &H00
ldi R17, $68
Low_blank_delay_12:
dec R17
brne low_blank_delay_12
'PULSE HIGH'
Timer0 = &H00
Video = Black
ldi R17, $1F
High_blank_delay:
dec R17
brne high_blank_delay
NOP
'PULSE LOW'
Video = 0
Timer0 = &H00
ldi R17, $FF
Low_blank_delay_2:
dec R17
brne low_blank_delay_2
Timer0 = &H00
ldi R17, $86
Low_blank_delay_21:
dec R17
brne low_blank_delay_21
NOP
'PULSE HIGH'
Timer0 = &H00
Video = Black
ldi R17, $1F
High_blank_delay_2:
dec R17
brne high_blank_delay_2
NOP
'PULSE LOW'
Video = 0
Timer0 = &H00
ldi R17, $FF
Low_blank_delay_3:
dec R17
brne low_blank_delay_3
Timer0 = &H00
ldi R17, $86
Low_blank_delay_31:
dec R17
brne low_blank_delay_31
NOP
'PULSE HIGH'
Timer0 = &H00
Video = Black
ldi R17, $1F
High_blank_delay_3:
dec R17
brne high_blank_delay_3
NOP
'PULSE LOW'
Video = 0
Timer0 = &H00
ldi R17, $16
Low_blank_delay:
dec R17
brne low_blank_delay
NOP
Linecount = 0
Goto Exit_isr
End If
Exit_isr:
' Timer affects high durations, as 'Black' will make Csync 1.
' Timer is set such that ISR will trigger just on time to make Csync 0
' When it re-enters the ISR.
Timer0 = &H75
Video = Black
ReturnWhen programming the board be sure to remember to upload as INTEL HEX and set fuse settings in the Xgpro software used by the TL866II.
Select INTEL HEX when you open the .hex file you compiled in BASCOM-AVR

For fuse settings, the ATTINY2313 datasheet on page 30 has the settings layed out…

The relevant settings are on the config tab in the software. As we are using a 20MHz external crystal we want CKSEL to be 1111 and SUT as 10 (20Mhz Crystal Osc. w/ fast rising power)

It is important the fuse settings are correct! You can brick your AVR if set incorrectly =[