/* Copyright (C) 1989, 1992 Aladdin Enterprises. All rights reserved. Distributed by Free Software Foundation, Inc. This file is part of Ghostscript. Ghostscript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to anyone for the consequences of using it or for whether it serves any particular purpose or works at all, unless he says so in writing. Refer to the Ghostscript General Public License for full details. Everyone is granted permission to copy, modify and redistribute Ghostscript, but only under the conditions described in the Ghostscript General Public License. A copy of this license is supposed to have been given to you along with Ghostscript so you can know your rights and responsibilities. It should be in a file named COPYING. Among other things, the copyright notice and this notice must be preserved on all copies. */ /* gp_atari.c */ /* Atari-specific routines for Ghostscript */ #include "memory_.h" #include "string_.h" #include "gx.h" #include "gp.h" #include "stat_.h" #include "time_.h" #include /* popen isn't POSIX-standard, so we declare it here. */ extern FILE *popen(); extern int pclose(); /* Do platform-dependent initialization */ void gp_init() { } /* Do platform-dependent cleanup */ void gp_exit() { } /* ------ Date and time ------ */ /* Read the current date (in days since Jan. 1, 1980) */ /* and time (in milliseconds since midnight). */ void gp_get_clock(long *pdt) { long secs_since_1970, secs_since_1980; struct tm *tm, *localtime(); if ( (secs_since_1970 = time(NULL)) == 0 ) { perror("Ghostscript: gettimeofday failed:"); exit(-1); } /* secs_since_1970 is #secs since Jan 1, 1970 */ /* subtract off number of seconds in 10 years */ /* leap seconds are not accounted for */ secs_since_1980 = secs_since_1970 - (long)(60 * 60 * 24 * 365.25 * 10); /* there is no time zone adjustment for the atari st */ /* adjust for daylight savings time - assume dst offset is 1 hour */ tm = localtime(&(secs_since_1970)); if ( tm->tm_isdst ) secs_since_1980 += (60 * 60); /* divide secs by #secs/day to get #days (integer division truncates) */ pdt[0] = secs_since_1980 / (60 * 60 * 24); /* modulo * 1000 gives number of millisecs since midnight */ pdt[1] = (secs_since_1980 % (60 * 60 * 24)) * 1000; #ifdef DEBUG_CLOCK printf("secs_since_1970 = %d usecs_since_1970 = %d pdt[0] = %ld\ pdt[1] = %ld\n", secs_since_1970, pdt[1], pdt[0], pdt[1]); #endif } /* ------ Screen management ------ */ /* Write a string to the console. */ void gp_console_puts(const char *str, uint size) { fwrite(str, 1, size, stdout); } /* Make the console current on the screen. */ int gp_make_console_current(struct gx_device_s *dev) { return 0; } /* Make the graphics current on the screen. */ int gp_make_graphics_current(struct gx_device_s *dev) { return 0; } /* ------ Printer accessing ------ */ /* Open a file or a printing device. If gp_open_printer returns * a NULL, the file will be sent directly to the centronics port. * This happens if fname = "CEN:" or if gp_open_scratch_file() * fails. Usually, GS wants to interpret a NULL return value as * an error, so this is slightly incompatible. * "|command" opens an output pipe. */ FILE * gp_open_printer(char *fname) { if (!strcmp(fname, "CEN:")) { /* Direct Centronics printing. */ return NULL; } else { return (strlen(fname) == 0 ? gp_open_scratch_file(gp_scratch_file_name_prefix, fname, "wb") : fname[0] == '|' ? popen(fname + 1, "wb") : fopen(fname, "wb")); } } /* Close the connection to the printer. */ void gp_close_printer(FILE *pfile, const char *fname) { if ( fname[0] == '|' ) pclose(pfile); else fclose(pfile); } /* ------ File name syntax ------ */ /* Define the character used for separating file names in a list. */ char gp_file_name_list_separator = ','; /* Define the default scratch file name prefix. */ const char gp_scratch_file_name_prefix[] = "gs_pr."; /* Define whether case is insignificant in file names. */ const int gp_file_names_ignore_case = 1; /* Define the string to be concatenated with the file mode */ /* for opening files without end-of-line conversion. */ const char gp_fmode_binary_suffix[] = "b"; /* Define the file modes for binary reading or writing. */ const char gp_fmode_rb[] = "rb"; const char gp_fmode_wb[] = "wb"; /* Create and open a scratch file with a given name prefix. */ /* Write the actual file name at fname. */ FILE * gp_open_scratch_file(const char *prefix, char *fname, const char *mode) { strcpy(fname, prefix); strcat(fname, "XXX"); mktemp(fname); return fopen(fname, mode); } /* Answer whether a file name contains a directory/device specification, */ /* i.e. is absolute (not directory- or device-relative). */ int gp_file_name_is_absolute(const char *fname, uint len) { /* A file name is absolute if it contains a drive specification */ /* (second character is a :) or if it start with / or \. */ return ( len >= 1 && (*fname == '/' || *fname == '\\' || (len >= 2 && fname[1] == ':')) ); } /* Answer the string to be used for combining a directory/device prefix */ /* with a base file name. The file name is known to not be absolute. */ const char * gp_file_name_concat_string(const char *prefix, uint plen, const char *fname, uint len) { if ( plen > 0 ) switch ( prefix[plen - 1] ) { case ':': case '/': case '\\': return ""; }; return "\\"; } /* ------ File operations ------ */ /* If the file given by fname exists, fill in its status and return 1; */ /* otherwise return 0. */ int gp_file_status(const char *fname, file_status *pstatus) { struct stat sbuf; /* The RS/6000 prototype for stat doesn't include const, */ /* so we have to explicitly remove the const modifier. */ if ( stat((char *)fname, &sbuf) < 0 ) return 0; pstatus->size_pages = stat_blocks(&sbuf); /* st_blocks is */ /* missing on some systems, */ /* see stat_.h */ pstatus->size_bytes = sbuf.st_size; pstatus->time_referenced = sbuf.st_mtime; pstatus->time_created = sbuf.st_ctime; return 1; } /* ------ File enumeration ------ */ /****** THIS IS NOT SUPPORTED ON UNIX SYSTEMS. ******/ /* Amazingly enough, there is no standard Unix library routine */ /* for enumerating the files matching a pattern, */ /* or even for enumerating (conveniently) the files in a directory. */ struct file_enum_s { char *pattern; int first_time; gs_memory_procs mprocs; }; /* Initialize an enumeration. NEEDS WORK ON HANDLING * ? \. */ file_enum * gp_enumerate_files_init(const char *pat, uint patlen, proc_alloc_t palloc, proc_free_t pfree) { file_enum *pfen = (file_enum *)(*palloc)(1, sizeof(file_enum), "gp_enumerate_files"); char *pattern; if ( pfen == 0 ) return 0; pattern = (*palloc)(patlen + 1, 1, "gp_enumerate_files(pattern)"); if ( pattern == 0 ) return 0; memcpy(pattern, pat, patlen); pattern[patlen] = 0; pfen->pattern = pattern; pfen->mprocs.alloc = palloc; pfen->mprocs.free = pfree; pfen->first_time = 1; return pfen; } /* Enumerate the next file. */ uint gp_enumerate_files_next(file_enum *pfen, char *ptr, uint maxlen) { if ( pfen->first_time ) { pfen->first_time = 0; } return -1; } /* Clean up the file enumeration. */ void gp_enumerate_files_close(file_enum *pfen) { proc_free_t pfree = pfen->mprocs.free; (*pfree)(pfen->pattern, strlen(pfen->pattern) + 1, 1, "gp_enumerate_files_close(pattern)"); (*pfree)((char *)pfen, 1, sizeof(file_enum), "gp_enumerate_files_close"); } /* * The remainder of this file contains printer output routines, most of * which were contributed by Chris Strunk (some of them were written by me). * I made some modifications to the following code (with permission), and * may have introduced errors not in the original code. * Tim Gallivan, 3/92. */ /* * This file is Copyright (C) 1990 Christoph Strunk. * * You are not allowed to copy or modify it. */ #define ATARI_TOS (1) #define MAKE_VOID (void *) #define MAXLEN (256) void con_flush(); int OutputIsAscii = 0, RTX_Found = 0; long *old_ssp = NULL; FILE *OutFile = NULL; #if PC_DOS || ATARI_TOS # define DIRECT 1 /* 1 = Direct Centronics port programming */ #endif #if ATARI_TOS || ( PC_DOS && DIRECT ) static short prn_out ( int ); #endif #if ATARI_TOS && LATTICE #include #endif #ifdef __GNUC__ #include #endif #if PC_DOS && DIRECT #include #endif /* output one character */ static int fatal_output_error = 0; void lputc(c) int c; { static short rc; static unsigned char global_count = 0; /* other processes every 256 chars */ void l_stop(), fatal(); if ( fatal_output_error ) return; global_count += 1; c &= 255; /* New in 2.9.44: avoid signed char problems */ #if ATARI_TOS || PC_DOS if ( ( c == '\n' ) && OutputIsAscii ) { lputc ( '\r' ); /* recursion */ } #endif if ( OutFile ) { rc = ( fputc ( c, OutFile ) == EOF ); } else { #if ATARI_TOS || ( PC_DOS && DIRECT ) rc = prn_out ( c ); #else rc = -1; #endif } if ( rc ) { if ( OutFile ) { perror ( "\nlputc" ); fprintf ( stderr, "\nOutput error -- %s ?\n", "Disk full or printer not ready" ); fclose ( OutFile ); OutFile = NULL; } else { fprintf ( stderr, "\nOutput error -- Printer not ready ?\n" ); } l_stop(); fatal_output_error = 1; fatal ( "Output error" ); } #if ATARI_TOS if ( RTX_Found && ! global_count ) call_yield(); /* allow other processes */ #endif } /* output a string */ void lputs ( s ) char *s; { while ( *s ) lputc ( *s++ ); } void lflush() { if ( OutFile ) fflush ( OutFile ); } /* start/stop lputc device */ void l_start() { void l_stop(), fatal(); #if ATARI_TOS && DIRECT volatile char *gpip = (char *) 0xFFFFFA01L; int cnt; if ( OutFile == NULL && old_ssp == NULL ) { old_ssp = (void *) call_super ( NULL ); cnt = 0; /* for ( cnt=0; ( *gpip & 1 ) && ( ++cnt <= 10 ); ) { printf("cnt = %d\n", cnt); */ Ongibit(0x20); /* set strobe bit */ /* } */ if ( cnt > 10 ) { l_stop(); puts ( "\n" ); fatal_output_error = 1; fatal ( "Printer not ready" ); } } #endif #if ATARI_TOS && ! DIRECT int cnt; if ( OutFile == NULL && old_ssp == NULL ) { old_ssp = (void *) call_super ( NULL ); for ( cnt=0; ( ! prt_ready(0) ) && ( ++cnt <= 10 ); ) { Ongibit(0x20); /* set strobe bit */ } if ( cnt > 10 ) { l_stop(); puts ( "\n" ); fatal_output_error = 1; fatal ( "Printer not ready" ); } } #endif #if PC_DOS && DIRECT if ( OutFile == NULL && ( biosprint ( 2, 0, BiosChannel ) & 0x29 ) ) { l_stop(); puts ( "\n" ); fatal_output_error = 1; fatal ( "Printer not ready" ); } #endif } void l_stop() { lflush(); #if ATARI_TOS if ( old_ssp != NULL ) { MAKE_VOID call_super ( old_ssp ); old_ssp = NULL; } #endif } #if ATARI_TOS && DIRECT extern void int_off __PROTO( ( void ) ); /* int_off: ori.w #$0700,sr * ret */ extern void int_on __PROTO( ( void ) ); /* int_on: andi.w #$F8FF,sr * ret */ static short prn_out ( c ) int c; { volatile unsigned long *hz_200; register unsigned long end_time; volatile char *gpip = (char *) 0xFFFFFA01L; register char g; #if OLD unsigned char loop_count = 0; #endif if ( old_ssp == NULL ) l_start(); hz_200 = (unsigned long *) 0x04BAL; #if OLD end_time = *hz_200 + 200; /* check once per second */ #else end_time = *hz_200 + 2; /* check 100 times per second */ #endif while ( *gpip & 1 ) { /* wait */ #if OLD /* Printer 1 sec. or more not ready ? */ if ( ( end_time < *hz_200 ) || ( ( ( ++loop_count & 7 ) == 0 ) && ( BatchMode || RTX_Found ) ) ) { con_flush(); /* allow Control_C, other Processes etc. */ end_time = *hz_200 + 2; /* check 100 times per second */ } #else if ( end_time <= *hz_200 ) { con_flush(); /* allow Control_C, other Processes etc. */ end_time = *hz_200 + 1; /* check 200 times per second */ } #endif } int_off(); /* disable interrupts */ gpip = (char *) 0xFFFF8800L; /* load sound chip adress now */ *gpip = 15; /* select port B */ gpip[2] = (char) c; /* write out char */ *gpip = 14; /* select port A */ g = *gpip; /* get old value */ #if OLD g &= 0xDF; /* clear strobe bit */ #else g &= ~0x20; /* clear strobe bit */ #endif gpip[2] = g; g |= 0x20; /* set strobe bit */ g |= 0x20; /* short delay */ gpip[2] = g; int_on(); /* enable interrupts */ return 0; } #endif #if ATARI_TOS && ! DIRECT static short prn_out ( c ) int c; { volatile unsigned long *hz_200 = (unsigned long *) 0x04BAL; register unsigned long end_time; if ( old_ssp == NULL ) l_start(); end_time = *hz_200 + 2; /* check 200 times per second */ while ( ! prt_ready(0) ) { /* wait */ if ( end_time <= *hz_200 ) { con_flush(); /* allow Control_C, other Processes etc. */ end_time = *hz_200 + 1; /* check 200 times per second */ } } prt_out ( 0, c ); return 0; } #endif #if PC_DOS && DIRECT static short prn_out ( c ) int c; { while ( biosprint ( 0, c, BiosChannel ) & 0x29 ) { /* wait until Ok or Control-C pressed */ con_flush(); } return 0; } #endif void con_flush() { int chin; void fatal(); chin = Crawio(0xFF); if ((chin & 0xFF) == 3) fatal("Keyboard Interrupt"); } void fatal(char *message) { fprintf(stderr, "%s\n", message); l_stop(); exit(-1); } /* The following routines are generic interfaces to Chris Strunk's * fast printing routines. Tim Gallivan 3/92. */ int csputc(int c, FILE *stream) { OutFile = stream; l_start(); lputc(c); l_stop(); return c; } int csputs(const char *s, FILE *stream) { OutFile = stream; l_start(); lputs(s); l_stop(); return 1; } int cswrite(const void *ptr, size_t size, size_t nobj, FILE *stream) { int count; if (stream == NULL) { OutFile = stream; l_start(); for (count=0; count < size*nobj; count++) { lputc(*(char *)(ptr++)); /* send the data */ } l_stop(); return count; } else { int ret; ret = fwrite(ptr, size, nobj, stream); con_flush(); return ret; } } int csprintf(FILE *stream, char *format, ...) { va_list args; char line[MAXLEN]; va_start(args, format); if (stream == NULL) { OutFile = stream; vsprintf(line, format, args); l_start(); lputs(line); l_stop(); va_end(args); return 0; } else { vfprintf(stream, format, args); va_end(args); con_flush(); return 0; } } /* #-----------------------------------------------------------------------------# #: Simple support routines for LATTICE C 5.04.01 and other C compilers # #: Lattice C Assembler # #: The parameter comes on the stack for functions starting with '_'. # #: void *call_super ( void * ); supervisor mode on/off # #: Original code by Chris Strunk. Modified for use with gcc in #: Ghostscript by Tim Gallivan, 3/92. #-----------------------------------------------------------------------------# */ asm("\ .text 0 .globl _int_off .globl _int_on .globl _call_super .globl _call_yield .globl _call_nice .globl _user_trace .globl _prt_ready .globl _prt_out _get_la: moveml d2/a2,sp@- .byte 0xA0,0x00 | Adresse Line-A-Var nach A0 & D0 moveml sp@+,d2/a2 rts _int_off: oriw #0x0700,sr rts _int_on: andiw #0xF8FF,sr rts _call_super: movel a7@(4),a0 moveml d2/a2-a3,sp@- movel a0,sp@- movew #0x20,sp@- movel a7,a3 | save current stack pointer trap #1 movel a3,a7 | make bad stack pointer good again !!!!!!! addql #6,sp movel d0,a0 | return value in both: d0 and a0 moveml sp@+,d2/a2-a3 rts _call_yield: moveml d2/a2,sp@- movew #0x00ff,sp@- | MiNT Syield() -- TOS illegal (rc -32) trap #1 addql #2,sp moveml sp@+,d2/a2 rts _call_nice: movel a7@(4),d0 moveml d2/a2,sp@- movew d0,sp@- movew #0x10A,sp@- trap #1 addql #4,sp moveml sp@+,d2/a2 rts _user_trace: moveml d1-d7/a0-a6,sp@- moveql #0,d0 movel 0x3F0,a1 | TEMPLMON user trace vector cmpal #0,a1 | not installed ? beqs end_trace jsr a1@ | execute it ! extl d0 end_trace: moveml sp@+,d1-d7/a0-a6 rts _prt_ready: moveml d3/a3/a5,a7@- subl a5,a5 movew d0,a7@- movel 0x506,a0 jsr a0@ addql #2,a7 moveml a7@+,d3/a3/a5 rts _prt_out: moveml d3/a3/a5,a7@- subl a5,a5 movew d1,a7@- movew d0,a7@- movel 0x50A,a0 jsr a0@ addql #4,a7 moveml a7@+,d3/a3/a5 rts ");