/*=========================================================================== Program : BROWSE.C65 Desc : Download file browser for BBS Express! Pro Author : Keith Ledbetter Written : August, 1989 Owner : Copyright (c) 1989 by Orion Micro Systems. All rights reserved. Compiler: C/65 C Cross-Compiler from Orion Micro Systems. ---------------------------------------------------------------------------- Change history Date Programmer Description -------- ------------ ----------------------- 8-20-89 K. Ledbetter Initial release (as version 3.0) ============================================================================*/ #version $32 #address $7000 #include runtime.h /* C/65 runtime header */ #include bbs_pro.h /* BBS Pro globals/routines */ #define TEMP_IOCB 2 #define DIR_IOCB 6 /*==================================================================* * Global data definitions * *==================================================================*/ char stack_pointer, /* holds stack at entry */ current_sub, /* sub we're on now */ file_line [50], /* where file data is at */ sub_name [40], /* holds current directory */ mark_name [15], /* holds batch dataset */ physical_SIG, /* file SIG we're doing */ logical_SIG, /* what it appears as */ SIG_drive; /* where current SIG is */ /* these fields are filled in by the "get_next_file" routine */ char ff_filename [15], /* ie: "FILENAME.EXT" */ ff_fullname [30], /* ie: "D1:F002>FILENAME.EXT" */ ff_descname [35]; /* ie: "D1:F002>DESC>FILENAME.EXT" */ int ff_dltime, ff_sectors; int total_marked; /* total files marked */ char sf_name [15]; int sf_date; byte catalog_mode; /* 1 means in catalog mode */ char buffer [1000]; char linetable [300]; /* 10 entries * 30 length */ char filetable [150]; /* 10 entries * 15 length */ char fulltable [250]; /* 10 entries * 25 length */ char desctable [380]; /* 10 entries * 30 length */ int tf_line [10]; /* 10 pointers to names */ int tf_filename [10]; int tf_fullname [10]; int tf_descname [10]; int tf_dltime [10]; int tf_sectors[10]; char logical_sigs [32]; /* logical SIG layout */ main() { byte x, y; #asm { tsx stx stack_pointer } init_file_table(); for (x = 0; x < 32; ++x) logical_sigs [x] = 0; for (x = 1, y = 0; x < 33; ++x) { if (s_sigdr [x-1] > 0 && sec_lvl (u_sigread, x) > 0) logical_sigs [y++] = x; } if (bbs_active != 0) { do { get_SIG_selection(); if (physical_SIG) process_SIG(); } while (physical_SIG > 0); } } exit_program() { MIOsuspend(); close (TEMP_IOCB); close (DIR_IOCB); MIOresume(); #asm { ldx stack_pointer txs } } /*==================================================================* * Ask the user which SIG they'd like to access. * *==================================================================*/ get_SIG_selection() { char tempstr [3]; byte signo; physical_SIG = 0; while ( 1 ) { checktime(); echof ("Enter which file area (L = List)? "); get_string (tempstr, 2, 0, 1); cr_lf(); if (length (tempstr) == 0) return; signo = valb (tempstr); if (signo == 0) { list_sigs(); cr_lf(); } else if (signo > 32 || logical_sigs [signo-1] == 0) echof ("Invalid selection!%e%e"); else { logical_SIG = signo; physical_SIG= logical_sigs [signo-1]; return; } } } /*==================================================================* * List the numbers and names of all SIGs this user can access. * *==================================================================*/ list_sigs () { byte x, y, z, count, psize; char *cp; aborted = 0; count = 0; cr_lf(); psize = u_pagesize - 1; for (x = 0; x < 32; ++x) { if (aborted) break; if (logical_sigs [x]) { if (x < 9) echo (' '); y = x + 1; echof ("%d. ", y); cp = logical_sigs [x] - 1 * 21 + &s_signames; echos (cp); ++count; if (u_vw == 40) { cr_lf(); if (count % psize == 0) { echos ("[- More -]"); get_key(); for (y = 0; y < 10; ++y) echof ("%c %c", 126, 126); } } else { if (count % 3 == 0) cr_lf(); else for (y = 0; y < 23 - *cp; ++y) echo (' '); } } } cr_lf(); } /*==================================================================* * Display a file SIG name from the SYSDATA.DAT table. * *==================================================================*/ show_name (char sig) { echos ( sig - 1 * 21 + &s_signames); } process_SIG() { byte x, m, d, y, oldmins; char command [5]; MIOsuspend(); goto_main_cat(); clear_marks(); MIOresume(); while ( 1 ) { checktime(); current_sub = 0; echof ("%e[%d] ", logical_SIG); show_name (physical_sig); echos (": "); get_string (command, 1, 0, 0); cr_lf(); case ( toupper (command [1]) ) { 'B' : { get_search_mask(); sf_date = 0; browse_files ( 1, 0 ); } 'Q' : { get_search_mask(); sf_date = 0; browse_files ( 0, 0 ); } 'C' : { get_search_mask(); sf_date = 0; browse_files ( 0, 1 ); } 'R' : { get_search_mask(); sf_date = 0; raw_list(); } '?' : helpfile ("FILEMENU"); 'V' : view_marked_files(); 'N' : { strcpy (sf_name, "*.*"); sf_date = calc_search_date(u_lastmm,u_lastdd,u_lastyy); browse_files (1, 0); } '/' : { if ( can_leave() ) { x = next_SIG(); if (x == 0) echose("No more SIGs!"); else { logical_SIG = x; physical_SIG= logical_sigs [logical_SIG-1]; echof("Going to SIG #%d%e", logical_sig); MIOsuspend(); goto_main_cat(); clear_marks(); MIOresume(); } } } 'X' : { if ( can_leave() ) { physical_SIG = 0; return; } } 'K' : { MIOsuspend(); clear_marks(); MIOresume(); echof ("All marks cleared%e"); } 'U' : { oldmins = min_conn; get_upload(); aborted = 0; min_conn = oldmins; } '=' : { if ( can_leave() ) return; } 'Y' : send_marked_files(); 'S' : { echof ("Files since Month: "); get_string (in_str, 2, 1, 0); m = valb (in_str); echof (" Day: "); get_string (in_str, 2, 1, 0); d = valb (in_str); echof (" Year: "); get_string (in_str, 2, 1, 0); y = valb (in_str); cr_lf(); strcpy (sf_name, "*.*"); sf_date = calc_search_date(m, d, y); browse_files (1); } else: echose ("Enter ? for help"); } } } raw_list() { aborted = 0; cr_lf(); while (get_next_file() && aborted == 0) echof("%s%e", file_line); } browse_files (byte verbose; byte catalog) { char command [3], *filler; int adr; byte total_files, ch, x, done, show_again; aborted = 0; if (u_vw == 40) filler = ""; else filler = " "; echo (125); if (catalog == 0) { if (strcmp (sf_name, "*.*") != 0 || sf_date > 0) echof ("Searching...%e%e"); } while ( 1 ) { if (catalog) { echo (125); cr_lf(); for (total_files = 0; total_files < 10; ++total_files) { if ( get_next_file() == 0 ) break; ch = total_files + 'A'; file_line [0] = 28; strcpy (tf_line [total_files], file_line); strcpy (tf_filename [total_files], ff_filename); strcpy (tf_fullname [total_files], ff_fullname); strcpy (tf_descname [total_files], ff_descname); tf_dltime [total_files] = ff_dltime; tf_sectors [total_files] = ff_sectors; echof ("%s [%c] %s%e", filler, ch, tf_line [total_files]); } if (total_files == 0) { echof ("End of files...%e"); return; } } else { if ( get_next_file() == 0 ) { echof ("End of files%e"); break; } } done = 0; if (catalog == 0) show_file_stats (verbose); do { checktime(); echof ("%eSubcommand: "); get_string (command, 1, 0, 1); if (length(command) == 0) strcpy (command, "C"); cr_lf(); case (toupper (command [1])) { 'C' : done = 1; 'D' : { if (get_selection (total_files, catalog)) download_file(); } 'A' : { if (catalog) { echo (125); cr_lf(); for (x = 0; x < total_files; ++x) { ch = x + 'A'; echof ("%s [%c] %s%e", filler, ch, tf_line [x]); } } else show_file_stats (verbose); } 'B' : { if (get_selection (total_files, catalog)) show_file_stats ( 1 ); } 'M' : { if (get_selection (total_files, catalog)) mark_file(); } 'V' : view_marked_files(); 'Y' : send_marked_files(); 'Q' : { MIOsuspend(); close (DIR_IOCB); MIOresume(); return; } 'X' : { MIOsuspend(); close (DIR_IOCB); MIOresume(); return; } /* 'R' : { aborted = 0; if (get_selection (total_files, catalog)) { dumpfile (ff_fullname); if (aborted == 0) increment_accesses(); aborted = 0; cr_lf(); } }*/ 'T' : { if (get_selection (total_files, catalog)) delete_file(); } 'E' : { if (get_selection (total_files, catalog)) { if (sec_lvl (u_sigedit, physical_SIG) == 0) echof ("You can't edit this..%e"); else { strcpy (parm1, ff_descname); adr = physical_SIG - 1 * 21 + &s_signames; strcpy (parm2, adr); overlay ("EDITDESC"); MIOresume(); } } } 'U' : { if (get_selection (total_files, catalog)) list_ARCfile(); } '?' : list_subcommands(); else: echof ("Enter '?' for help%e"); } } while (done == 0); if (catalog > 0 && total_files < 10) { echof ("End of files...%e"); return; } } } get_selection (byte total; byte catalog_mode) { byte max; char temp [3]; if (catalog_mode == 0) /* if not cataloging, done */ return (1); max = 'A' + total - 1; echof ("Which file [A - %c]? ", max); get_string (temp, 1, 0, 1); cr_lf(); if (length (temp) == 0) return(0); temp [1] = toupper (temp [1]); if (temp [1] < 'A' || temp [1] > max) { echof("Invalid selection!%e"); return(0); } max = temp [1] - 'A'; strcpy (file_line, tf_line [max]); strcpy (ff_filename, tf_filename [max]); strcpy (ff_fullname, tf_fullname [max]); strcpy (ff_descname, tf_descname [max]); ff_dltime = tf_dltime [max]; ff_sectors = tf_sectors [max]; return(1); } list_ARCfile() { strcpy (parm1, ff_fullname); if ( pos (ff_filename, ".ARC") ) overlay ("ARCV"); else if ( pos (ff_filename, ".ALF") ) overlay ("ARCV"); else if ( pos (ff_filename, ".LZH") ) overlay ("LZHV"); else if ( pos (ff_filename, ".ZIP") ) overlay ("ZIPV"); else if ( pos (ff_filename, ".ZOO") ) overlay ("ZOOV"); else echof ("I can only handle the following\archives: ARC ALF LZH ZIP ZOO.%e"); } show_file_stats (byte verbose) { byte x, ch; if (verbose) echo(125); echof ("%e%s %s%e", inverse(" File:"), ff_filename); echof ("%s ", inverse(" Size:")); for (x=13; x < 20; ++x) { ch = file_line [x]; if (ch != ' ') echo (ch); } echof (" bytes (%d sectors)%e", ff_sectors); echof ("%s ", inverse(" Date:")); for (x=21; x < 36; ++x) { ch = file_line [x]; if (x > 21 || ch != ' ') echo (ch); } echof ("%e%s %d min. at ", inverse(" Time:"), ff_dltime); case (baud) { 0 : echos ("300"); 1 : echos ("1200"); 2 : echos ("2400"); 3 : echos ("4800"); 4 : echos ("9600"); else: echos ("19200"); } echose (" baud"); if (verbose) show_description(); } show_description() { int idx, buffer_size; byte got_desc; got_desc = 0; MIOsuspend(); if (s_usedesc) { file_stat (ff_descname); if (open (TEMP_IOCB, ff_descname, 4, 0) == OK) { buffer_size = blockread (TEMP_IOCB, buffer, sizeof (buffer)); if (IOresult (TEMP_IOCB) == 136) got_desc = 1; } } close (TEMP_IOCB); MIOresume(); if (got_desc == 0) { echof("%e%eDescription not available...%e%e"); return; } idx = 0; if (buffer [0] == $de) { /* new-style description */ echof("%s ", inverse(" Owner:")); echof("%s%e", buffer + 3); echof("%s ", inverse(" D/L's:")); echof("%d%e", buffer [2] * 256 + buffer [1]); idx = 46; } cr_lf(); aborted = 0; while (idx < buffer_size && aborted == 0) echo (buffer [idx++]); cr_lf(); aborted = 0; } increment_accesses() { echof("%eUpdating download counter...%e"); MIOsuspend(); file_stat (ff_descname); if (open (TEMP_IOCB, ff_descname, 12, 0) == OK) { in_str [0] = 0; blockread (TEMP_IOCB, in_str, 3); if (in_str [0] == $de) { in_str [1] = in_str [1] + 1; if (in_str [1] == 0) in_str [2] = in_str [2] + 1; point_zero(); blockwrite (TEMP_IOCB, in_str, 3); } } close (TEMP_IOCB); MIOresume(); } /*==================================================================* * Change to the master subdirectory of this file SIG. * *==================================================================*/ goto_main_cat () { strcpy (in_str, "Dx:>PRO>FILES_xx"); SIG_drive = s_sigdr [physical_SIG - 1]; in_str [2] = SIG_drive; in_str [15] = physical_SIG / 10 + '0'; in_str [16] = physical_SIG % 10 + '0'; file_stat (in_str); if ( fcwd (in_str) != OK ) fatal_error("Directory not found!"); strcpy (mark_name, "Dx:BATCH.DAT"); mark_name [2] = SIG_drive; } fatal_error (char *s) { MIOresume(); echose (s); exit_program(); } /*==================================================================* * Clear out the marked files dataset. NOTE: that this * * routine DOES NOT suspend the modem -- do it before! * *==================================================================*/ clear_marks() { total_marked = 0; file_stat (mark_name); open (TEMP_IOCB, mark_name, 8, 0); close (TEMP_IOCB); } get_search_mask() { echof ("Enter filemask to search on\(Return for '*.*'): "); get_string (sf_name, 12, 0, 1); if ( length (sf_name) == 0 ) strcpy (sf_name, "*.*"); } say_no_time() { echof ("%eYou don't have enough time for that!%e"); } minutes_left () { int left_this_call, left_today; left_this_call = u_tlcall - min_conn; left_today = u_tlday - u_mintoday - min_conn; if (left_this_call < left_today) return ( left_this_call + dl_tolerance ); else return ( left_today + dl_tolerance ); } mark_file () { char temp [30]; MIOsuspend(); if (open (TEMP_IOCB, mark_name, 9, 0) == OK) { strcpy (temp, " ") temp [0] = 22 - length (ff_fullname); fprintf(TEMP_IOCB, "%s%s%d Minutes%e", ff_fullname, temp, ff_dltime); if (IOresult (TEMP_IOCB) == OK) ++total_marked; close (TEMP_IOCB); } MIOresume(); echof ("File marked%e"); } next_SIG () { byte x; for (x = logical_SIG + 1; x < 33; ++x) { if (logical_sigs [x-1] > 0) return (x); } return(0); } can_leave() { if (total_marked == 0) return(1); echose ("You have files marked for batch\downloading. If you exit, these"); echose ("marks will all be cleared."); cr_lf(); echos ("Do you really want to exit? "); if (y_or_n() == 'Y') return(1); return(0); } get_next_file() { int fd; byte m,d,y; MIOsuspend(); while ( 1 ) { if (current_sub == 0) { if (goto_next_sub() == 0) { MIOresume(); return (0); } } if (inputsd (DIR_IOCB, file_line) != OK) { if (goto_next_sub() == 0) { MIOresume(); return (0); } continue; } if (length (file_line) < 35 || file_line [14] == '<') continue; if (sf_date) { m = 0; if (file_line [21] > 32) m = file_line [21] - '0' * 10; m = file_line [22] - '0' + m; d = file_line [24] - '0' * 10; d = file_line [25] - '0' + d; y = file_line [27] - '0' * 10; y = file_line [28] - '0' + y; fd = y - 85 * 365; fd = m * 30 + d + fd; if (fd < sf_date) continue; } format_ff_data(); MIOresume(); return (1); } } format_ff_data() { byte x, y, ch; for (x=1, y=1; x < 13; ++x) { ch = file_line [x]; if (ch == ' ') continue; if (x == 10) ff_filename [y++] = '.'; ff_filename [y++] = ch; } ff_filename [0] = y - 1; strcpy (ff_fullname, sub_name); ff_fullname [0] = 8; /* remove the file mask */ strcpy (ff_descname, ff_fullname); strcat (ff_fullname, ff_filename); strcat (ff_descname, "DESC>"); strcat (ff_descname, ff_filename); ff_dltime = time_to_dl (file_line); ff_sectors = dl_sectors (file_line); } goto_next_sub() { ++current_sub; if (current_sub > 127) return (0); strcpy (sub_name, "Dx:F001>"); sub_name [2] = SIG_drive; sub_name [5] = current_sub / 100 + '0'; sub_name [6] = current_sub % 100 / 10 + '0'; sub_name [7] = current_sub % 100 % 10 + '0'; strcat (sub_name, sf_name); file_stat (sub_name); if (open (DIR_IOCB, sub_name, 6, 128) != OK) return (0); } delete_file() { byte rc; if (sec_lvl (u_sigdel, physical_SIG) == 0) { echof ("You can't delete files!%e"); return; } echof ("Delete...are you sure? "); if (y_or_n() != 'Y') return; MIOsuspend(); rc = fdelete (ff_fullname); MIOresume(); if (rc == 1) echof ("File deleted%e"); else { echof ("Unable to delete file!%e"); return; } MIOsuspend(); rc = fdelete (ff_descname); MIOresume(); if (rc == 1) echof ("Description deleted%e"); else echof ("Unable to delete description!%e"); } view_marked_files() { if (total_marked == 0) echof ("You don't have any files marked!%e"); else { echof ("Current marked files:%e%e"); dumpfile (mark_name); } } point_zero() { #asm { ldx #$20 lda #37 ;poknt to 0th byte sta $342,x lda #0 sta $34c,x sta $34d,x sta $34e,x jsr $e456 } } list_subcommands() { echos (" C - Continue D - Download \"); echose(" U - UnARC File M - Mark file"); echos (" Q - Quit (or X) A - Show again \"); echose(" T - Delete file V - View marked"); echos (" Y - Send marked \"); echose(" E - Edit file description"); echose(" B - Show description of a file"); } calc_search_date (byte m, d, y) { int fd; fd = y - 85 * 365; fd = m * 30 + d + fd; return (fd); } send_marked_files() { if ( total_marked == 0 ) { echof ("You don't have any files marked!%e"); return; } echof ("Send marked files%e"); strcpy (parm1, mark_name); aborted = 0; overlay ("XMBATCH"); MIOsuspend(); if (open_log_file()) { fprintf(TEMP_IOCB, " Batch downloaded %d files%e", total_marked); close_log_file(); } clear_marks(); MIOresume(); } download_file() { if (minutes_left() < ff_dltime) { echof ("You don't have enough time for that!%e"); return; } strcpy (parm1, "S"); strcpy (parm2, ff_fullname); aborted = 0; overlay ("SFXFER"); MIOsuspend(); if (open_log_file()) { if (aborted) fprintf(TEMP_IOCB, " Aborted download of %s%e", ff_filename); else fprintf(TEMP_IOCB, " Downloaded %s from SIG #%d%e", ff_filename, physical_SIG); close_log_file(); } MIOresume(); if (aborted == 0) increment_accesses(); } get_upload() { int adr; if (s_uploadr == 0) { echof("Uploads are turned off!%e"); return; } show_freespace(); while ( 1 ) { echof ("Enter a filename for your upload%e->"); get_string (ff_filename, 12, 0, 1); if (length (ff_filename) == 0) return; if (pos (ff_filename, ".") == 0 || ff_filename [length(ff_filename)] == '.') { echof ("%eEnter an extender; ie: file.COM%e%e"); continue; } if (valid_name ()) break; else echof("%eInvalid filename (must be A-Z 0-9)%e%e"); } strcpy (ff_fullname, "Dx:>PRO>UPLOADS>"); ff_fullname [2] = s_uploadr; strcpy (ff_descname, ff_fullname); strcat (ff_fullname, ff_filename); strcat (ff_descname, "DESC>"); strcat (ff_descname, ff_filename); MIOsuspend(); if (open (TEMP_IOCB, ff_fullname, 4, 0) == OK) { close (TEMP_IOCB); MIOresume(); echof("That file already exists!%e"); return; } aborted = 0; strcpy (parm1, "R"); strcpy (parm2, ff_fullname); overlay ("XMODEM"); MIOsuspend(); if (open_log_file()) { if (aborted) fprintf(TEMP_IOCB, " Aborted upload of %s%e", ff_filename); else fprintf(TEMP_IOCB, " Uploaded %s to SIG #%d%e", ff_filename, physical_SIG); close_log_file(); } if (aborted) fdelete (ff_fullname); MIOresume(); if (aborted == 0) { n_uploads = 1; if (s_usedesc) { strcpy (parm1, ff_descname); adr = physical_SIG - 1 * 21 + &s_signames; strcpy (parm2, adr); overlay ("EDITDESC"); MIOresume(); } } } show_freespace() { float f1, f2; char dm [12]; byte x, y; echof ("Free disk space: "); strcpy (dm, "Dx:ZZXX.ZZ"); dm [2] = s_uploadr; parm1 [0] = 0; MIOsuspend(); if ( open (TEMP_IOCB, dm, 6, 128) == OK ) { for (x = 1; x < 6; ++x) { inputsd (TEMP_IOCB, parm1); if (IOresult (TEMP_IOCB) != OK) { parm1 [0] = 0; break; } } } close (TEMP_IOCB); MIOresume(); strtof(parm1, f1); strtof("256", f2); fmult (f1, f1, f2); ftostr (f1, parm1); if (length (parm1) > 8) parm1 [0] = 8; strcpy (parm2, " , , "); for (x = length(parm1), y = 10; x > 0; --x) { if (y == 7 || y == 3) --y; parm2 [y--] = parm1 [x]; } if (parm2 [6] == ' ') parm2 [7] = ' '; if (parm2 [2] == ' ') parm2 [3] = ' '; for (x = 1; x <= length (parm2); ++x) { if (parm2 [x] > ' ') echo (parm2 [x]); } echof (" bytes%e%e"); } open_log_file() { if (s_logdr > 0) { strcpy (in_str, "Dx:>PRO>LOG>CALL.LOG"); in_str [2] = s_logdr; if (open(TEMP_IOCB, in_str, 9, 0) == OK) return(1); } return(0); } close_log_file() { close (TEMP_IOCB); } time_to_dl (char *instr) { #asm { ; Calculate the time to download a file. ; Call from Action and pass the ENTIRE input ; line from the "directory" entry. ; ; Byte Func TimeToDL (String Instr) ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ sta .ptr stx .ptr+1 lda baud asl tay lda .bauds,y sta _inbuff lda .bauds+1,y sta _inbuff+1 lda #0 ;Action calls to here sta _cix jsr _afp ;divide by bytes/minute jsr _fmove ;fr1 = fr0 lda .ptr sta _inbuff lda .ptr+1 sta _inbuff+1 lda #13 ;size is 13 into the line sta _cix jsr _afp ;filesize to fp jsr _fdiv ;divide it by 128 jsr _fpi ;convert to integer lda _fr0 ora _fr0+1 bne .notzero inc _fr0 bne .notzero inc _fr0+1 .notzero: lda _fr0 ldx _fr0+1 rts .ptr: dc.b 0,0 .bauds: dc.w .b300 dc.w .b1200 dc.w .b2400 dc.w .b4800 dc.w .b9600 dc.w .b19200 .b300: dc.b "1800",$9b .b1200: dc.b "7200",$9b .b2400: dc.b "14400",$9b .b4800: dc.b "28800",$9b .b9600: dc.b "57600",$9b .b19200: dc.b "99000",$9b } } dl_sectors (char *instr) { #asm { ; Calculate the number of single density ; sectors in a file. Pass the ENTIRE input ; line from the "directory" entry. ; ; Card Func DL_Sectors (String Instr) ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ sta .ptr stx .ptr+1 lda #0 sta _cix lda #.cc128 & $ff sta _inbuff lda #.cc128 / 256 sta _inbuff+1 jsr _afp ;float it jsr _fmove lda .ptr sta _inbuff lda .ptr+1 sta _inbuff+1 lda #13 ;size is 13 into the line sta _cix jsr _afp ;filesize to fp jsr _fdiv ;divide it by 128 jsr _fpi ;convert to integer inc _fr0 bne .novf inc _fr0+1 .novf: lda _fr0 ldx _fr0+1 rts .ptr: dc.b 0,0 .cc128: dc.b "128",$9b } } valid_name() { #asm { lda ff_filename sta .lth ldy #1 .loop: cpy .lth beq .goon bcs .file_ok .goon: lda ff_filename,y jsr upcase cmp #'_' beq .char_ok cmp #'.' beq .char_ok cmp #'0' bcc .file_bad cmp #'9'+1 bcc .char_ok cmp #'A' bcc .file_bad cmp #'Z'+1 bcs .file_bad .char_ok: iny bne .loop .file_ok: lda #1 ldx #0 rts .file_bad: lda #0 tax rts .lth: ds.b 1 } } init_file_table() { #asm { ldy #0 .loop: lda .tfline,y sta tf_line,y lda .tffile,y sta tf_filename,y lda .tffull,y sta tf_fullname,y lda .tfdesc,y sta tf_descname,y iny cpy #20 bcc .loop rts .tfline: dc.w 0 * 30 + linetable dc.w 1 * 30 + linetable dc.w 2 * 30 + linetable dc.w 3 * 30 + linetable dc.w 4 * 30 + linetable dc.w 5 * 30 + linetable dc.w 6 * 30 + linetable dc.w 7 * 30 + linetable dc.w 8 * 30 + linetable dc.w 9 * 30 + linetable .tffile: dc.w 0 * 15 + filetable dc.w 1 * 15 + filetable dc.w 2 * 15 + filetable dc.w 3 * 15 + filetable dc.w 4 * 15 + filetable dc.w 5 * 15 + filetable dc.w 6 * 15 + filetable dc.w 7 * 15 + filetable dc.w 8 * 15 + filetable dc.w 9 * 15 + filetable .tffull: dc.w 0 * 25 + fulltable dc.w 1 * 25 + fulltable dc.w 2 * 25 + fulltable dc.w 3 * 25 + fulltable dc.w 4 * 25 + fulltable dc.w 5 * 25 + fulltable dc.w 6 * 25 + fulltable dc.w 7 * 25 + fulltable dc.w 8 * 25 + fulltable dc.w 9 * 25 + fulltable .tfdesc: dc.w 0 * 30 + desctable dc.w 1 * 30 + desctable dc.w 2 * 30 + desctable dc.w 3 * 30 + desctable dc.w 4 * 30 + desctable dc.w 5 * 30 + desctable dc.w 6 * 30 + desctable dc.w 7 * 30 + desctable dc.w 8 * 30 + desctable dc.w 9 * 30 + desctable } }