; SERVID - Serial video display using Ubicom SX microcontroller ; $Id: servid.asm,v 1.34 2001/01/31 07:25:31 eric Exp $ ; ; Copyright 2000, 2001 Eric Smith <eric@brouhaha.com> ; ; Home page: ; http://www.brouhaha.com/ubicom/servid/ ; ; This program is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License version 2 as published ; by the Free Software Foundation. Note that permission is not granted ; to redistribute this program under the terms of any other version of the ; General Public License. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to the Free Software ; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ; ; NOTE: it is sometimes claimed that compliance with the GPL is ; awkward for commercial interests. Licenses for non-GPL use of this ; program may be negotiated with the author. ; ; This program is written to be assembled with the GPASM assembler, ; version 0.8.14 or newer: ; http://gpasm.sourceforge.net/ ; NOTE: there are references in this code to PAL and NTSC. Technically ; those are color standards. In most cases the references to PAL and NTSC ; are really intended to refer to 625/50 and 525/59.94 scanning, or (in ; non-interlaced mode) 312/25 and 262/29.97 scanning. ;--------------------------------------------------------------------------- ; feature test switches ;--------------------------------------------------------------------------- ft_sx28 equ 0 ; 0 for SX18/SX20, 1 for SX28 ft_pal_video equ 0 ; 0 for NTSC 525/60, 1 for PAL 625/50 ; (approximate timing only) ; (not well tested) ft_interlace equ 1 ; 1 for interlaced video ft_color equ 0 ; 1 for color burst (NTSC only) ft_serial_input equ 1 ; 1 for normal serial input, ; 0 to omit (when replaced with user ; application code) ft_ser_noninv equ 1 ; 0 for "normal" TTL-level serial, ; mark = low, space = high ; 1 for non-inverted serial (the ; crude resistor-only method) ; mark = high, space = low ft_splash equ 1 ; 1 for splash screen ;--------------------------------------------------------------------------- ; processor definitions and assembler settings ;--------------------------------------------------------------------------- if ft_sx28 processor SX28 else processor SX18 endif radix dec errorlevel -305 ; don't want default destination warning errorlevel -224 ; don't want deprecated instruction warnings, ; since on an SX or PIC16C5X there's no other ; way to set the TRIS or OPTION registers include "sxdefs.inc" device equ pins28+pages4+banks8+oschs+optionx+stackx+bor40+turbo ; for instruction destination argument f equ 1 w equ 0 int_off equ 0xc3 ; RTCC internal clock, prescale by 16, ; RTCC interrupt off, WDT disabled int_on equ 0x83 ; RTCC internal clock, prescale by 16, ; RTCC interrupt on, WDT disabled ;--------------------------------------------------------------------------- ; other includes ;--------------------------------------------------------------------------- include "ascii.inc" ;--------------------------------------------------------------------------- ; video definitions ;--------------------------------------------------------------------------- ; Display size in characters. Note that simply changing these definitions ; won't have the desired effect. rows equ 4 columns equ 20 ; osc = 42.9545 MHz = 12 * color burst ; tCYC = 23.2804 ns ; theoretical total width = 12 * 227.5 = 2730 cycles = 2 * 3 * 5 * 91 = 15 * 182 ; ; The RTCC prescaler can only be set for powers of two, and we need the ; count to be a little under 256, so we use a prescaler of 16 and a divisor ; of 171, for an actual scan line width of 2736 cycles (63.7 us). h equ 2736 ; 63.7 us hsync_pulse_width equ 201 ; 4.7 us equalization_pulse_width equ 98 ; 2.3 us serration_pulse_width equ 201 ; 4.7 us vsync_pulse_width equ (h/2)-serration_pulse_width front_porch_width equ 64 ; 1.5 us back_porch_width equ 193 ; 4.5 us ; safe area = 40 us = 1718 cycles ; 20 chars wide * (5+2) = 139 pixels wide, 12.4 cycles per pixel ; ("rounded" up to 13) ; ; for 4:3 aspect ratio, display should be 90 pixels tall, so make ; a pixel be 3 scan lines. scan_lines_per_vpixel equ 3 vpixels_per_char equ 10 chars_per_row equ 20 if ft_pal_video total_active_lines equ 287 else total_active_lines equ 242 endif active_video_lines equ rows*vpixels_per_char*scan_lines_per_vpixel top_border equ (total_active_lines-active_video_lines)/2 bottom_border equ total_active_lines-(top_border+active_video_lines) ;--------------------------------------------------------------------------- ; I/O port definitions ;--------------------------------------------------------------------------- rxd_bit equ 0 pzt_bit equ 1 txd_bit equ 2 ; not used mode_button_bit equ 3 ; not used #define rxd porta,rxd_bit #define pzt porta,pzt_bit #define txd porta,txd_bit #define mode_button porta,mod_button_bit trisa equ 0x01 ; RxD is our only input inita equ 0x00 trisb equ 0x00 ; all outputs initb equ vid_sync if ft_sx28 trisc equ 0x00 ; all outputs initc equ 0x00 ; drive low endif ;--------------------------------------------------------------------------- ; composite video definitions ;--------------------------------------------------------------------------- vid_port equ portb ; D/A converter ; DAC 0 = ground (sync tip), 255 = 1.25V into 75 ohm load ; one DAC step = 1.25/255 V = 4.902 mV ; there are 140 IRE units to 1.0V, so an IRE unit is 7.143 mV = 1.457 DAC steps vid_sync equ 0 ; -40 IRE vid_blank equ 58 ; 58.29 = 0 IRE vid_black equ 69 ; 69.21 = 7.5 IRE vid_white equ 204 ; 204.00 = 100 IRE vid_max_chroma equ 249 ; 249.17 = 131 IRE burst_amplitude equ 58 ; 58.29 = 40 IREs ;--------------------------------------------------------------------------- ; bell definitions ;--------------------------------------------------------------------------- ; The bell tone is nominally around 500 Hz for 200 ms (100 cycles). ; This works out to a period of 32 scan lines. bell_half_period equ 16 ; lines bell_duration equ 200 ; half-periods ;--------------------------------------------------------------------------- ; serial definitions ;--------------------------------------------------------------------------- ; While serial line idle, sample every scan line. Once start bit is ; detected, delay 6 scan lines, then sample every 13. This results ; in a 1208 bps rate, 0.6% fast. lines_per_serial_sample equ 13 skip_on_ser_rx_mark macro if ft_ser_noninv btfss rxd else btfsc rxd endif endm skip_on_ser_rx_space macro if ft_ser_noninv btfsc rxd else btfss rxd endif endm ;--------------------------------------------------------------------------- ; memory utilization ;--------------------------------------------------------------------------- rambase equ 08h ; start of RAM rombase equ 0000h ; beginning of program romsize equ 0800h chargen equ romsize-384 resetvec equ romsize-1 ; reset vector intvec equ 0000h ; interrupt vector main_page equ 0000h int_page equ 0200h ;--------------------------------------------------------------------------- ; shared variables ;--------------------------------------------------------------------------- org rambase ; start of RAM g_field_count: res 1 ; field down-counter g_mtemp: res 1 ; global temp for main ; "DelM" uses "DelMCnt" in the interupt. Do NOT use DelM or DelMCnt in main! DelMCnt: res 1 ; counter used for cycle delays Five: res 1 Fifteen res 1 ;--------------------------------------------------------------------------- ; variables for main ;--------------------------------------------------------------------------- org 010h main_vars: temp: res 3 char: res 1 ; character being processed escape_state: res 1 ; 0 = normal ; 1 = ESC seen, waiting for 2nd char ; 2 = ESC-Y seen, waiting for <col> ; 3 = ESC-Y <col> seen, waiting for <row> esc_Y_col: res 1 ; cursor cursor_col: res 1 cursor_row: res 1 cursor_loc: res 1 ; for scrolling src_addr equ temp dest_addr equ temp+1 move_count equ temp+2 ;--------------------------------------------------------------------------- ; variables for interrupt ;--------------------------------------------------------------------------- org 030h int_vars: line_type: res 1 ; type of scan line we're working on ; 2 * [0 .. line_types-1] line_count: res 1 ; how many lines of this type to do if ft_color burst_phase: res 1 ; LSB used for burst phase int_temp: res 1 ; general use in interrupt endif line_start: res 1 ; start buffer loc of currently displayed line char_ptr: res 1 ; pointer to currently displayed character chargen_ptr: res 2 ; pointer into character generator inverse_flag: res 1 ; bit 7 indicates current char inverse vpix_cnt: res 1 ; vertical pixel counter scanline_cnt: res 1 ; vertical scan line counter (per pixel) char_cnt: res 1 pixels: res 1 ; pixels of current char ; bell bell_half_cyc: res 1 ; bell half-cycle in lines bell_line_cnt: res 1 ; bell half-cycle down-counter bell_dur_cnt: res 1 ; bell duration ;--------------------------------------------------------------------------- ; variables for serial receive ;--------------------------------------------------------------------------- if ft_serial_input org 050h ser_vars: ser_rx_state: res 1 ser_rx_byte: res 1 ser_rx_samp_cnt: res 1 ser_rx_bit_cnt: res 1 ser_rx_char: res 1 ser_rx_flag: res 1 endif ;--------------------------------------------------------------------------- ; video buffer ;--------------------------------------------------------------------------- ; NOTE: subtract offset of 20h (space) before storing characters into ; video buffer video_buffer equ 070h ; 80 characters, uses last five banks ; *must* start on a bank boundary ; reserve RAM, skipping over banks as needed res_bank_ram macro count local c c set count while c>0 res 1 if ($ & 010h)==0 org $+010h endif c set c-1 endw endm org video_buffer line_0: res_bank_ram columns line_1: res_bank_ram columns line_2: res_bank_ram columns-1 line_2_end: res_bank_ram 1 line_3: res_bank_ram columns-1 line_3_end: res_bank_ram 1 org rombase page interrupt ; 0 goto interrupt ; 1 escape_state_table: movf escape_state,w addwf pcl goto esc_not_seen goto esc_seen goto esc_Y_col_seen goto esc_Y_row_seen control_char_table: movf char,w addwf pcl goto null ; 00 - NUL - null - don't do anything goto null ; 01 - goto null ; 02 - goto null ; 03 - goto null ; 04 - goto null ; 05 - goto null ; 06 - goto bell ; 07 - BEL - bell goto backspace ; 08 - BS - backspace goto null ; 09 - goto line_feed ; 0a - LF - line feed goto null ; 0b - goto form_feed ; 0c - FF - form feed - clear screen goto carriage_return ; 0d - CR - carriage return goto null ; 0e goto null ; 0f goto null ; 10 goto null ; 11 goto null ; 12 goto null ; 13 goto null ; 14 goto null ; 15 goto null ; 16 goto null ; 17 goto null ; 18 goto null ; 19 goto null ; 1a goto escape ; 1b - ESC - escape goto null ; 1c goto null ; 1d goto null ; 1e goto null ; 1f esc_char_table: addwf pcl goto bad_escape ; 40 - @ goto cursor_up ; 41 - A - cursor up goto cursor_down ; 42 - B - cursor down goto cursor_left ; 43 - C - cursor left goto cursor_right ; 44 - D - cursor right goto bad_escape ; 45 - E goto bad_escape ; 46 - F goto bad_escape ; 47 - G goto home_cursor ; 48 - H - cursor home goto rev_line_feed ; 49 - I - reverse line feed (can scroll) goto clear_eop ; 4A - J - clear to end of screen goto clear_eol ; 4B - K - clear to end of line goto bad_escape ; 4C - L goto bad_escape ; 4D - M goto bad_escape ; 4E - N goto bad_escape ; 4F - O goto bad_escape ; 50 - P goto bad_escape ; 51 - Q goto bad_escape ; 52 - R goto bad_escape ; 53 - S goto bad_escape ; 54 - T goto bad_escape ; 55 - U goto bad_escape ; 56 - V goto bad_escape ; 57 - W goto bad_escape ; 58 - X goto esc_Y ; 59 - Y - cursor positioning goto bad_escape ; 5A - Z goto bad_escape ; 5B - [ goto bad_escape ; 5C - \ goto bad_escape ; 5D - ] goto bad_escape ; 5E - ^ goto bad_escape ; 5F - _ show_cursor: movf cursor_loc,w movwf fsr bsf indf,7 bank main_vars return hide_cursor: movf cursor_loc,w movwf fsr bcf indf,7 bank main_vars return ; delay until either the number of fields specified in W have been ; displayed (zero flag set), or a serial character is received ; (zero flag clear) delay_fields: bank ser_vars movwf g_field_count df_loop: if ft_serial_input movf ser_rx_flag ; check serial receive flag btfss status,zf ; character received? goto df_return ; yes, return to caller endif movf g_field_count ; has field count decremented to zero? btfss status,zf goto df_loop ; no, keep looping df_return: bank main_vars return home_cursor: clrf cursor_row carriage_return: clrf cursor_col compute_cursor_loc: movf cursor_row,w ; cursor_loc = 20 * cursor_row movwf cursor_loc bcf status,cf rlf cursor_loc rlf cursor_loc swapf cursor_row,w addwf cursor_loc movf cursor_col,w ; cursor_loc += cursor_col addwf cursor_loc movf cursor_loc,w ; shift high nibble left one bit andlw 0f0h addwf cursor_loc movlw video_buffer ; add in base address addwf cursor_loc null: return ; output a character from W to the display output_char: andlw 07fh ; strip MSB (parity?) and save movwf char goto escape_state_table ; process character esc_not_seen: movf char,w andlw 060h ; is it a control character? btfsc status,zf goto control_char_table ; yes, process and return movf char,w ; is it a DEL xorlw asc_del btfsc status,zf return ; yes, do nothing movf char,w ; fall into printable_char printable_char: movwf g_mtemp ; save character movlw -' ' ; remove offset addwf g_mtemp movf cursor_loc,w ; store character movwf fsr movf g_mtemp,w movwf indf bank main_vars ; fall into cursor_advance cursor_advance: incf cursor_col movf cursor_col,w xorlw columns btfss status,zf goto compute_cursor_loc crlf: clrf cursor_col line_feed: incf cursor_row movf cursor_row,w xorlw rows btfss status,zf goto compute_cursor_loc decf cursor_row ; restore call compute_cursor_loc scroll_up: movlw line_1 movwf src_addr movlw line_0 movwf dest_addr movlw (rows-1)*columns movwf move_count call block_move_up movlw line_3 ; clear freed space movwf temp+1 movlw columns movwf temp goto clear_chars block_move_up: movf src_addr,w movwf fsr movf indf,w movwf g_mtemp bank main_vars incf src_addr bsf src_addr,4 movf dest_addr,w movwf fsr movf g_mtemp,w movwf indf bank main_vars incf dest_addr bsf dest_addr,4 decfsz move_count goto block_move_up return backspace: decf cursor_col btfss cursor_col,7 goto compute_cursor_loc movlw columns-1 movwf cursor_col rev_line_feed: decf cursor_row btfss cursor_row,7 goto compute_cursor_loc incf cursor_row ; restore call compute_cursor_loc scroll_down: movlw line_2_end movwf src_addr movlw line_3_end movwf dest_addr movlw (rows-1)*columns movwf move_count call block_move_down movlw line_0 ; clear freed space movwf temp+1 movlw columns movwf temp goto clear_chars block_move_down: movf src_addr,w movwf fsr movf indf,w movwf g_mtemp bank main_vars decf src_addr btfsc src_addr,4 goto bmd_1 movlw 010h subwf src_addr bmd_1: movf dest_addr,w movwf fsr movf g_mtemp,w movwf indf bank main_vars decf dest_addr btfsc dest_addr,4 goto bmd_2 movlw 010h subwf dest_addr bmd_2: decfsz move_count goto block_move_down return clear_eol: movlw columns ; compute number of chars to clear: movwf temp ; temp := columns - cursor_col movf cursor_col,w subwf temp movf cursor_loc,w movwf temp+1 ; clear temp chars starting at loc temp+1 clear_chars: movlw ' '-020h movwf g_mtemp ; fill temp chars starting at loc temp+1 to value temp+2 fill_chars: movf temp+1,w movwf fsr movf g_mtemp,w movwf indf bank main_vars incf temp+1 bsf temp+1,4 decfsz temp goto fill_chars return form_feed: call home_cursor clear_eop: call clear_eol ; clear to end of current line movlw rows-1 ; compute additional rows to clear: movwf temp ; temp := (rows - 1) - cursor_row movf cursor_row,w subwf temp btfsc status,zf ; any rows to clear? return ; no bcf status,cf ; multiply temp by 20 to get char count rlf temp bcf status,cf rlf temp movf temp,w bcf status,cf rlf temp bcf status,cf rlf temp addwf temp goto fill_chars cursor_up: decf cursor_row movlw rows-1 btfsc cursor_row,7 movwf cursor_row goto compute_cursor_loc cursor_down: incf cursor_row btfsc cursor_row,2 ; hard-coded for 4 rows clrf cursor_row goto compute_cursor_loc cursor_left: decf cursor_col movlw columns-1 btfsc cursor_col,7 movwf cursor_col goto compute_cursor_loc cursor_right: incf cursor_col movf cursor_col,w xorlw columns btfsc status,zf clrf cursor_row goto compute_cursor_loc esc_Y: movlw 2 movwf escape_state bad_escape: return esc_Y_col_seen: movlw ' ' subwf char,w movwf esc_Y_col incf escape_state return escape: incf escape_state return esc_Y_row_seen: movlw (256-' ')-rows ; range check the row (still has ' ' offset) addwf char,w btfsc status,cf goto bad_row movlw ' ' ; move cursor to specified column subwf char,w movwf cursor_row bad_row movlw 256-columns ; range check the column addwf esc_Y_col,w btfsc status,cf goto bad_col movf esc_Y_col,w ; move cursor to specified column movwf cursor_col bad_col: clrf escape_state goto compute_cursor_loc esc_seen: clrf escape_state ; assume only two-char sequence movlw '@' subwf char,w movwf temp andlw 060h ; check for range 40-5F btfss status,zf goto bad_escape movf temp,w goto esc_char_table bell: bank int_vars ; start a bell movlw bell_half_period movwf bell_half_cyc movwf bell_line_cnt movlw bell_duration movwf bell_dur_cnt bank main_vars return reset: mode 0fh ; paranoia movlw int_off option bank main_vars movlw inita movwf porta movlw trisa tris porta movlw initb movwf portb movlw trisb tris portb if ft_sx28 movlw initc movwf portc movlw trisc tris portc endif clrf escape_state call form_feed bank int_vars movlw 5 ; set up for DelM macro movwf Five movlw 15 movwf Fifteen clrf line_type incf line_type,w ; get initial line count page line_dispatch call line_dispatch page $ movwf line_count if ft_serial_input bank ser_vars clrf ser_rx_state movlw 1 movwf ser_rx_samp_cnt clrf ser_rx_flag endif bank main_vars movlw int_on option if ft_splash page splash call splash endif ; call home_cursor main_loop: call show_cursor movlw 30 call delay_fields if ft_serial_input btfss status,zf goto got_char endif call hide_cursor movlw 30 call delay_fields if ft_serial_input btfss status,zf goto got_char endif goto main_loop if ft_serial_input got_char: call hide_cursor ; hide cursor during character processing bank ser_vars ; get character and clear rx flag movf ser_rx_char,w clrf ser_rx_flag bank main_vars call output_char goto main_loop endif ;--------------------------------------------------------------------------- ; interrupt handler ;--------------------------------------------------------------------------- org 0200h include "delm.inc" scanln macro count,function goto function retlw count endm ; table of line type function pointers and counts ; when called for function, takes cycles 58-63 line_dispatch: addwf pcl ; 58 if ft_pal_video ; PAL lines scanln 2,equalization_line ; 624-625 scanln 2,vsync_line ; 1-2 scanln 1,vsync_eq_line ; 3 scanln 2,equalization_line ; 4-5 scanln 17,vblank_line ; 6-22 scanln 1,vblank_black_line ; 23 scanln top_border,black_video_line ; 24-106 scanln active_video_lines,active_video_line ; 107-226 scanln bottom_border-1,black_video_line ; 227-309 if ft_interlace scanln 1,black_video_line ; 310 scanln 2,equalization_line ; 311-312 scanln 1,eq_vsync_line ; 313 scanln 2,vsync_line ; 314-315 scanln 2,equalization_line ; 316-317 scanln 1,eq_vblank_line ; 318 scanln 17,vblank_line ; 319-335 scanln top_border,black_video_line ; 336-418 scanln active_video_lines,active_video_line ; 419-538 scanln bottom_border,black_video_line ; 539-622 endif scanln 1,black_eq_line ; 310 or 623 else ; ; NTSC lines scanln 3,equalization_line ; 1-3 scanln 3,vsync_line ; 4-6 scanln 3,equalization_line ; 7-9 scanln 11,vblank_line ; 10-20 scanln top_border,black_video_line ; 21-81 scanln active_video_lines,active_video_line ; 82-201 scanln bottom_border,black_video_line ; 202-262 if ft_interlace scanln 1,black_eq_line ; 263 scanln 2,equalization_line ; 264-265 scanln 1,eq_vsync_line ; 266 scanln 2,vsync_line ; 267-268 scanln 1,vsync_eq_line ; 269 scanln 2,equalization_line ; 270-271 scanln 1,eq_vblank_line ; 272 scanln 10,vblank_line ; 273-282 scanln 1,vblank_black_line ; 283 scanln top_border,black_video_line ; 284-344 scanln active_video_lines,active_video_line ; 345-464 scanln bottom_border,black_video_line ; 465-525 endif endif line_types equ (($-line_dispatch)-1)/2 if ft_serial_input ; serial input state machine dispatch serial_state_table: movf ser_rx_state,w ; 12 addwf pcl ; 13-15 goto ser_idle ; 16-18 goto ser_data_bit ; 16-18 goto ser_stop_bit ; 16-18 endif bell_48: bcf pzt ; 48 delm 1 ; 49 bell_50: delm 1 ; 50 goto bell_done ; 51-53 interrupt: movlw vid_blank ; 4 start front porch movwf vid_port ; 5 if ft_serial_input bank ser_vars ; 6 decfsz ser_rx_samp_cnt ; 7 goto skip_serial ; 8-10 call serial_state_table ; 9-11 goto serial_done ; 39-41 skip_serial: delm 31 ; 11-41 serial_done: else delm 36 ; 6-41 endif bank int_vars ; 42 movf bell_dur_cnt ; 43 - bell active? btfsc status,zf ; 44 goto bell_48 ; 45-47 - no decfsz bell_line_cnt ; 46 - time to toggle PZT? goto bell_50 ; 47-49 - no movlw 1<<pzt_bit ; 48 - toggle PZT xorwf porta ; 49 decf bell_dur_cnt ; 50 - decrement duration movf bell_half_cyc,w ; 51 - reinit line count movwf bell_line_cnt ; 52 delm 1 ; 53 bell_done: movf line_type,w ; 54 call line_dispatch ; 55-57 decfsz line_count ; any more lines of the current type? goto interrupt_done ; no, done incf line_type ; advance to next line type incf line_type movf line_type,w ; end of field? xorlw line_types*2 btfss status,zf goto get_line_count ; no clrf line_type ; yes, start new field movf g_field_count ; decrement field counter btfss status,zf decf g_field_count get_line_count: incf line_type,w ; new line type, how many lines? call line_dispatch movwf line_count interrupt_done: movlw 256-171 ; all done with this scan line retiw ; at some point within the frame before the first active video line, ; call this subroutine to initialize the pointers display_frame_setup: movlw video_buffer movwf line_start clrf vpix_cnt movlw scan_lines_per_vpixel movwf scanline_cnt return if ft_color burst_l equ vid_blank-(burst_amplitude/2) burst_h equ vid_blank+(burst_amplitude/2) burst_x equ burst_l^burst_h ; burst starts at 228 cycles from horizontal reference point, ; which is 292 cycles from our start of back porch. color_burst: delm 14 ; 270-283 ; $$$ actually, don't toggle burst phase, because our current ; line timing is an integral multiple of the color carrier movlw 0 ; 284 - toggle burst phase xorwf burst_phase ; 285 movlw 18 ; 286 - 18 half-cycles of burst movwf int_temp ; 287 movlw burst_h ; 288 - assume leading edge high btfsc burst_phase,0 ; 289 movlw burst_l ; 290 burst_loop: xorlw burst_x ; 291 399 movwf vid_port ; 292 400 decfsz int_temp ; 293 401 goto burst_loop ; 294-296 402 delm 2 ; 403 movlw vid_blank ; 405 movwf vid_port ; 406 delm 48 ; 407 return ; 455-457 else color_burst: delm 185 ; 270-454 return ; 455-457 endif equalization_line: movlw vid_sync ; 64 - start equalizing pulse movwf vid_port delm equalization_pulse_width-2 movlw vid_blank ; end equalizing pulse movwf vid_port delm ((h/2)-equalization_pulse_width)-2 eq_second_half: movlw vid_sync ; start equalizing pulse movwf vid_port delm equalization_pulse_width-2 movlw vid_blank ; end equalizing pulse movwf vid_port return vsync_line: movlw vid_sync ; 64 - start vsync pulse movwf vid_port delm vsync_pulse_width-2 movlw vid_blank ; end vsync pulse - start serration movwf vid_port delm serration_pulse_width-2 vsync_second_half: movlw vid_sync ; start vsync pulse movwf vid_port delm vsync_pulse_width-2 movlw vid_blank ; end vsync pulse - start serration movwf vid_port return vblank_line: movlw vid_sync ; 64 - start hsync pulse movwf vid_port ; 65 delm hsync_pulse_width-2 ; 66-264 movlw vid_blank ; 265 - end hsync pulse movwf vid_port ; 266 call color_burst ; 267-269 return black_video_line: movlw vid_sync ; 64 - start hsync pulse movwf vid_port ; 65 delm hsync_pulse_width-2 ; 66-264 movlw vid_blank ; 265 - end hsync pulse, start back porch movwf vid_port ; 266 call color_burst ; 267-269 movlw vid_black ; 458 - end back porch, start active video movwf vid_port ; 459 goto display_frame_setup ; $$$ not the best place for this? ; return if ft_interlace|ft_pal_video ; NTSC line 263, PAL line 623 (interlaced), PAL line 310 (non-interlaced) black_eq_line: movlw vid_sync ; 64 - start hsync pulse movwf vid_port ; 65 delm hsync_pulse_width-2 ; 66-264 movlw vid_blank ; 265 - end hsync pulse, start back porch movwf vid_port ; 266 call color_burst ; 267-269 movlw vid_black ; 458 - end back porch, start active video movwf vid_port ; 459 delm ((h/2)-(hsync_pulse_width+back_porch_width))-5 goto eq_second_half endif if ft_interlace ; NTSC line 266, PAL line 313 eq_vsync_line: movlw vid_sync ; 64 - start equalizing pulse movwf vid_port delm equalization_pulse_width-2 movlw vid_blank ; end equalizing pulse movwf vid_port delm ((h/2)-equalization_pulse_width)-5 goto vsync_second_half endif if ft_interlace|ft_pal_video ; NTSC line 269, PAL line 3 vsync_eq_line: movlw vid_sync ; 64 - start vsync pulse movwf vid_port delm vsync_pulse_width-2 movlw vid_blank ; end vsync pulse - start serration movwf vid_port delm serration_pulse_width-5 goto eq_second_half endif if ft_interlace ; NTSC line 272 - like an equalization, but a full line with only one pulse ; PAL line 318 eq_vblank_line: movlw vid_sync ; 64 - start equalizing pulse movwf vid_port delm equalization_pulse_width-2 movlw vid_blank ; end equalizing pulse movwf vid_port return endif if ft_interlace|ft_pal_video ; NTSC line 283, PAL line 23 vblank_black_line: movlw vid_sync ; 64 - start hsync pulse movwf vid_port ; 65 delm hsync_pulse_width-2 ; 66-264 movlw vid_blank ; 265 - end hsync pulse movwf vid_port ; 266 call color_burst ; 267-269 delm ((h/2)+front_porch_width)-458 ; 458-1431 movlw vid_black ; 1432 - start active video movwf vid_port return endif active_video_line: movlw vid_sync ; 64 - start hsync pulse movwf vid_port ; 65 delm hsync_pulse_width-2 ; 66-264 movlw vid_blank ; 265 - end hsync pulse, start back porch movwf vid_port ; 266 call color_burst ; 267-269 movlw vid_black ; 458 - end back porch, start active video movwf vid_port ; 459 btfsc vpix_cnt,3 ; vpixel >= 8? goto active_video_line_done delm 100 movf line_start,w movwf char_ptr movlw chars_per_row+1 movwf char_cnt ; leading dummy character is always blank, allows us to fill ; the pixel pipeline clrf pixels character: ; pixel 0 movlw vid_black ; 0 btfsc pixels,0 ; 1 movlw vid_white ; 2 movwf vid_port ; 3 movf char_ptr,w ; 4 - get next character movwf fsr ; 5 movf indf,w ; 6 bank int_vars ; 7 movwf inverse_flag ; 8 andlw 07fh ; 9 movwf chargen_ptr ; 10 decf char_cnt,w ; 11 - increment buffer pointer btfss status,zf ; 12 - unless we're at end of line incf char_ptr ; 13 - (due to pipeline, we pass through bsf char_ptr,4 ; 14 - here columns+1 times) ; pixel 1 movlw vid_black ; 0 btfsc pixels,1 ; 1 movlw vid_white ; 2 movwf vid_port ; 3 movlw (chargen/4)&0ffh ; 4 - add low part of chargen base addwf chargen_ptr ; 5 movlw (chargen/4)>>8 ; 6 - add high part of chargen base movwf chargen_ptr+1 ; 7 btfsc status,cf ; 8 incf chargen_ptr+1 ; 9 bcf status,cf ; 10 - rotate high bit of vpix_cnt into btfsc vpix_cnt,2 ; 11 - table address bsf status,cf ; 12 rlf chargen_ptr ; 13 rlf chargen_ptr+1 ; 14 ; pixel 2 movlw vid_black ; 0 btfsc pixels,2 ; 1 movlw vid_white ; 2 movwf vid_port ; 3 bcf status,cf ; 4 - rotate next bit of vpix_cnt into btfsc vpix_cnt,1 ; 5 - table address bsf status,cf ; 6 rlf chargen_ptr ; 7 rlf chargen_ptr+1 ; 8 delm 6 ; 9-14 ; pixel 3 movlw vid_black ; 0 btfsc pixels,3 ; 1 movlw vid_white ; 2 movwf vid_port ; 3 DelM 11 ; 4-14 ; pixel 4 movlw vid_black ; 0 btfsc pixels,4 ; 1 movlw vid_white ; 2 movwf vid_port ; 3 movf chargen_ptr+1,w ; 4 movwm ; 5 movf chargen_ptr,w ; 6 iread ; 7-10 movwf pixels ; 11 movmw ; 12 mode 0fh ; 13 movwf chargen_ptr ; 14 - now use chargen_ptr as a temp ; intercharacter space btfsc vpix_cnt,0 ; 0 - get left four pixels into bits 0..3 swapf pixels ; 1 movlw vid_black ; 2 movwf vid_port ; 3 bcf pixels,4 ; 4 btfsc vpix_cnt,0 ; 5 - get rightmost pixel into bit 4 rrf chargen_ptr ; 6 btfsc chargen_ptr,0 ; 7 bsf pixels,4 ; 8 movlw 01fh ; 9 - invert if needed btfsc inverse_flag,7 ; 10 xorwf pixels ; 11 ; $$$ add more inter-character spacing here? decfsz char_cnt ; 12 goto character ; 13-15 active_video_line_done: decfsz scanline_cnt ; more scan lines for this pixel row? return movlw scan_lines_per_vpixel movwf scanline_cnt incf vpix_cnt ; more pixels for this character row? movf vpix_cnt,w xorlw vpixels_per_char btfss status,zf return clrf vpix_cnt movf char_ptr,w ; advance buffer pointer to next character row movwf line_start return ;--------------------------------------------------------------------------- ; serial receive routine ;--------------------------------------------------------------------------- if ft_serial_input ser_idle: movlw 1 ; 19 - sample every line movwf ser_rx_samp_cnt ; 20 skip_on_ser_rx_mark ; 21 - start bit detected? goto ser_ret_25 ; 22-24 - no, return movlw lines_per_serial_sample * 3 / 2 ; 23 movwf ser_rx_samp_cnt ; 24 - skip start bit and sample first data bit ; in middle of bit time movlw 8 ; 25 - set up to receive 8 chars movwf ser_rx_bit_cnt ; 26 clrf ser_rx_byte ; 27 - not needed for 8-bit chars incf ser_rx_state ; 28 incf ser_rx_flag ; 29 - signal main movlw (lines_per_serial_sample * 11)/8 ; 30 - set up for stop bit movwf ser_rx_samp_cnt ; 31 incf ser_rx_state ; 32 - advance to next state goto ser_ret_36 ; 33-35 ser_stop_bit: movlw 1 ; 19 - sample every line movwf ser_rx_samp_cnt ; 20 skip_on_ser_rx_mark ; 21 - line idle? clrf ser_rx_state ; 22 - yes, back to idle ser_ret_23: delm 2 ; 23-24 ser_ret_25: delm 4 ; 25-28 ser_ret_29: delm 3 ; 29-31 ser_ret_32: delm 4 ; 32-35 ser_ret_36: return ; 36-38 endif ;--------------------------------------------------------------------------- ; splash screen ;--------------------------------------------------------------------------- if ft_splash org 400h splash_table: addwf pcl dt asc_bel ; 01234567890123456789 dt "SERVID 0.2 Copyright" dt "2001 Eric Smith and", asc_cr, asc_lf dt "Richard Ottosen", asc_cr, asc_lf dt "(SXLIST challenge) " dt 0 splash: clrf temp+2 splash_loop: movf temp+2,w call splash_table xorlw 0 btfsc status,zf retp page output_char call output_char ; $$$ change to end with retp? page $ incf temp+2 goto splash_loop endif ;--------------------------------------------------------------------------- ; character generator macros ;--------------------------------------------------------------------------- org chargen cg_row1 macro pixels local char local col char set pixels r1_bits set 0 col set 0 while col<5 r1_bits set (r1_bits*2)+(char%10) char set char/10 col set col+1 endw endm cg_row2 macro pixels local char local col char set pixels r2_bits set 0 col set 0 while col<5 r2_bits set (r2_bits*2)+(char%10) char set char/10 col set col+1 endw dw ((r1_bits&010h)<<4)+(r1_bits&0fh)+((r2_bits&010h)<<5)+((r2_bits&0fh)<<4) endm include "charset.inc" org resetvec goto reset end
See: