Mail Archives: djgpp/1997/01/29/22:28:47
From: | Me <gmitch AT ime DOT net>
|
Newsgroups: | comp.os.msdos.djgpp
|
Subject: | Porting 16bit software for 32bit DJGPP
|
Date: | Wed, 29 Jan 1997 17:04:17 -0800
|
Organization: | Maine Medical Center
|
Lines: | 902
|
Message-ID: | <32EFF391.CB6@ime.net>
|
NNTP-Posting-Host: | mitchg-home.mmc.org
|
Mime-Version: | 1.0
|
To: | djgpp AT delorie DOT com
|
DJ-Gateway: | from newsgroup comp.os.msdos.djgpp
|
This is a multi-part message in MIME format.
--------------7E1E274D67D
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Can someone please help me with porting this 16 bit program to compoile
and run with djgpp v2? Thanks in advance.
--------------7E1E274D67D
Content-Type: text/plain; charset=us-ascii; name="SNDSCAPE.C"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="SNDSCAPE.C"
/****************************************************************************
ENSONIQ Soundscape Digital Audio Driver
DOS Real-Mode Version
Created 09/08/95, ENSONIQ Corp.
****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <dos.h>
#include "sndscape.h"
/****************************************************************************
EXTERNAL GLOBAL VARS - hardware config info ...
****************************************************************************/
WORD BasePort; /* Gate Array/MPU-401 base port */
short MidiIrq; /* the MPU-401 IRQ */
WORD WavePort; /* the CODEC base port */
short WaveIrq; /* the PCM IRQ */
short DmaChan; /* the PCM DMA channel */
/****************************************************************************
INTERNAL GLOBAL VARS - all kinds of stuff ...
****************************************************************************/
/* This var MUST be pre-initialized !!! */
BYTE Flags = 0x00; /* driver state flags */
#define FLAG_DETECTED 0x01
#define FLAG_OPENED 0x02
#define FLAG_CODECON 0x04
short Windx; /* Wave IRQ index - for reg writes */
short Mindx; /* MIDI IRQ index - for reg writes */
char * volatile NextBuffer; /* dynamic DMA A/B buffer pointer */
char * DmaBuffP; /* pointer to aligned dma buff */
DWORD DmaPaddr; /* physical DMA address */
char DmaBuffer[BUFFERSIZE*2]; /* the dma buffer space */
#ifdef USE_CODEC_IRQ
void (interrupt * VecSav)(); /* int vector save area */
WORD PicBase; /* the PIC base port */
short IrqMask; /* IRQ mask value */
short IrqMaskSav; /* save area for IRQ mask */
short Vector; /* the Wave interrupt vector num */
#endif
short CdCfgSav; /* gate array register save area */
short DmaCfgSav; /* gate array register save area */
short IntCfgSav; /* gate array register save area */
short DacSavL; /* DAC left volume save */
short DacSavR; /* DAC right volume save */
short CdxSavL; /* CD/Aux left volume save */
short CdxSavR; /* CD/Aux right volume save */
short const SsIrqs[4] = { 9, 5, 7, 10 }; /* Soundscape IRQs */
short const RsIrqs[4] = { 9, 7, 5, 15 }; /* an older IRQ set */
short const * Irqs; /* pointer to one of the IRQ sets */
DMAC_REGS const DmacRegs[4] =
{ /* the DMAC regs for chans 0-3 ... */
{ 0x00, 0x01, 0x08, 0x0a, 0x0b, 0x0c, 0x87 },
{ 0x02, 0x03, 0x08, 0x0a, 0x0b, 0x0c, 0x83 },
{ 0x04, 0x05, 0x08, 0x0a, 0x0b, 0x0c, 0x81 },
{ 0x06, 0x07, 0x08, 0x0a, 0x0b, 0x0c, 0x82 }
};
DMAC_REGS const * DmacRegP; /* a pointer to a DMAC reg struct */
/****************************************************************************
EXTERNAL DRIVER FUNCTIONS
****************************************************************************/
/*
short DetectSoundscape(void);
This function is used to detect the presence of a Soundscape card in a
system. It will read the hardware config info from the SNDSCAPE.INI file,
the path to which is indicated by the SNDSCAPE environment variable. This
config info will be stored in global variable space for the other driver
functions to reference. Once the config settings have been determined, a
hardware test will be performed to see if the Soundscape card is actually
present. If this function is not explicitly called by the application, it
it will be called by the OpenSoundscape function.
INPUTS:
None
RETURNS:
0 - if Soundscape detection was successful
-1 - if
The SNDSCAPE environment variable was not found
The SNDSCAPE.INI file cannot be opened
The SNDSCAPE.INI file is missing information or is corrupted
The Soundscape hardware is not detected
*/
short DetectSoundscape(void)
{
char static str[78];
short tmp, status;
char *ep;
FILE *fp;
/* default status to failure */
status = -1;
/* initialize some local vars ... */
fp = 0;
/* get the environment var and build the filename; then open it */
if( (ep = getenv("SNDSCAPE")) == NULL )
goto exit;
strcpy(str, ep);
if( str[strlen(str) - 1] == '\\' )
str[strlen(str) - 1] = '\0';
strcat(str, "\\SNDSCAPE.INI");
if( (fp = fopen(str, "r")) == NULL )
goto exit;
/* read all of the necessary config info ... */
if( GetConfigEntry(str, "Product", fp) )
goto exit;
/* if an old product name is read, set the IRQs accordingly */
strupr(str);
if( !strstr(str, "SOUNDFX") && !strstr(str, "MEDIA_FX") )
Irqs = SsIrqs;
else
Irqs = RsIrqs;
if( GetConfigEntry(str, "Port", fp) )
goto exit;
BasePort = (WORD) strtol(str, NULL, 16);
if( GetConfigEntry(str, "WavePort", fp) )
goto exit;
WavePort = (WORD) strtol(str, NULL, 16);
if( GetConfigEntry(str, "IRQ", fp) )
goto exit;
MidiIrq = (short) strtol(str, NULL, 10);
if( MidiIrq == 2)
MidiIrq = 9;
if( GetConfigEntry(str, "SBIRQ", fp) )
goto exit;
WaveIrq = (short) strtol(str, NULL, 10);
if( WaveIrq == 2 )
WaveIrq = 9;
if( GetConfigEntry(str, "DMA", fp) )
goto exit;
DmaChan = (short) strtol(str, NULL, 10);
/* see if Soundscape is there by reading HW ... */
if( (inp(BasePort + GA_HOSTCTL_OFF) & 0x78) != 0x00 )
goto exit;
if( (inp(BasePort + GA_ADDR_OFF) & 0xf0) == 0xf0 )
goto exit;
outp(BasePort + GA_ADDR_OFF, 0xf5);
tmp = inp(BasePort + GA_ADDR_OFF);
if( (tmp & 0xf0) == 0xf0)
goto exit;
if( (tmp & 0x0f) != 0x05)
goto exit;
/* now do a quick check to make sure the CoDec is there too */
if( (inp(WavePort) & 0x80) != 0x00 )
goto exit;
/* flag success !!! */
status = 0;
Flags |= FLAG_DETECTED;
exit:
if( fp != NULL )
fclose(fp);
return status;
}
/*
short OpenSoundscape(void);
This function opens the Soundscape driver. It will setup the Soundscape
hardware for Native PCM mode (the default mode is Sound Blaster emulation).
It will first call the DetectSoundscape function it it determines that it
has not already been called.
INPUTS:
None.
RETURNS:
0 - if successful
-1 - if the DetectSoundscape function needed to be called and
returned unnsuccessful
*/
short OpenSoundscape(void)
{
/* already opened? if so, success */
if( Flags & FLAG_OPENED )
return 0;
/* see if we need to detect first */
if( !(Flags & FLAG_DETECTED) )
if( DetectSoundscape() != 0 )
return -1;
/* Clear possible CODEC and SoundBlaster emulation interrupts */
outp(WavePort + CD_STATUS_OFF, 0x00);
inp(0x22e);
#ifdef USE_CODEC_IRQ
/* install the interrupt vector */
Vector = (WaveIrq < 8 ? 0x08 : 0x70) + (WaveIrq & 0x07);
VecSav = _dos_getvect(Vector);
_dos_setvect(Vector, WaveIsr);
/* enable the CODEC interrupt pin */
CdWrite(CD_PINCTL_REG, CdRead(CD_PINCTL_REG) | 0x02);
/* Setup PIC/IRQ vars, unmask our IRQ */
PicBase = WaveIrq < 8 ? 0x20 : 0xa0;
IrqMask = 1 << (WaveIrq & 0x07);
IrqMaskSav = inp(PicBase + 1) & IrqMask;
outp(PicBase + 1, inp(PicBase + 1) & ~IrqMask);
#else
/* disable the CODEC interrupt pin */
CdWrite(CD_PINCTL_REG, CdRead(CD_PINCTL_REG) & 0xfd);
#endif
/* Align the DMA buffer within the allocated space */
DmaBuffP = DmaBuffer;
DmaPaddr = (DWORD) (char *) DmaBuffer;
DmaPaddr = ((DmaPaddr >> 12) & 0x000ffff0) + (WORD) DmaPaddr;
if( DmaPaddr >> 16 != (DmaPaddr + BUFFERSIZE-1) >> 16 )
{
DmaPaddr += BUFFERSIZE;
DmaBuffP += BUFFERSIZE;
}
/* Set DMA controller register set pointer based on channel */
DmacRegP = &DmacRegs[DmaChan];
/* Save some regs, do some resource re-routing */
/* derive the MIDI and Wave IRQ indices (0-3) for reg writes */
for( Mindx = 0; Mindx < 4; ++Mindx )
if( MidiIrq == *(Irqs + Mindx) )
break;
for( Windx = 0; Windx < 4; ++Windx )
if( WaveIrq == *(Irqs + Windx) )
break;
/* setup the CoDec DMA polarity */
GaWrite(GA_DMACFG_REG, 0x50);
/* give the CoDec control of the DMA and Wave IRQ resources */
CdCfgSav = GaRead(GA_CDCFG_REG);
GaWrite(GA_CDCFG_REG, 0x89 | (DmaChan << 4) | (Windx << 1));
/* pull the Sound Blaster emulation off of those resources */
DmaCfgSav = GaRead(GA_DMAB_REG);
GaWrite(GA_DMAB_REG, 0x20);
IntCfgSav = GaRead(GA_INTCFG_REG);
GaWrite(GA_INTCFG_REG, 0xf0 | (Mindx << 2) | Mindx);
/* Save all volumes that we might use, init some levels */
DacSavL = CdRead(CD_DACL_REG);
DacSavR = CdRead(CD_DACR_REG);
CdxSavL = CdRead(CD_CDAUXL_REG);
CdxSavR = CdRead(CD_CDAUXL_REG);
/* Init the DAC volume to ... full */
SetDacVol(127, 127);
/* Put the CoDec into mode change state */
outp(WavePort + CD_ADDR_OFF, 0x40);
/* Setup CoDec mode - single DMA chan, AutoCal on */
CdWrite(CD_CONFIG_REG, 0x0c);
/* exit mode change state */
outp(WavePort + CD_ADDR_OFF, 0x00);
/* flag open status and return */
Flags |= FLAG_OPENED;
return 0;
}
/*
short StartCoDec(WORD srate, BOOL stereo, BOOL size16bit);
This function will start the CoDec auto-restart DMA process.
INPUTS:
srate - the audio sample rate in Hertz
stereo - FALSE for mono data, TRUE for L/R interleaved stereo data
size16bit - FALSE for 8-bit unsigned data, TRUE for 16-bit signed data
RETURNS:
0 - if the CoDec was started successfully
-1 - if the driver has not been opened or if the sample rate
is unacceptable
*/
short StartCoDec(WORD srate, BOOL stereo, BOOL size16bit)
{
DWORD i;
WORD tmp;
/* if the driver is not open, fail */
if( !(Flags & FLAG_OPENED) )
return -1;
/* make sure the CODEC is not already running */
if( Flags & FLAG_CODECON )
StopCoDec();
/* set format .... */
/* init the format register value */
tmp = 0;
/* find the sample rate */
switch(srate)
{
case 8000U:
tmp = 0x00;
break;
case 11025U:
tmp = 0x03;
break;
case 16000U:
tmp = 0x02;
break;
case 22050U:
tmp = 0x07;
break;
case 32000U:
tmp = 0x06;
break;
case 44100U:
tmp = 0x0b;
break;
case 48000U:
tmp = 0x0c;
break;
default:
return -1;
}
/* set other format bits ... */
if( stereo == TRUE )
tmp |= 0x10;
if( size16bit == TRUE )
tmp |= 0x40;
/* put the the CoDec in mode change state */
outp(WavePort + CD_ADDR_OFF, 0x40);
/* and write the format register */
CdWrite(CD_FORMAT_REG, tmp);
/* delay for internal re-synch */
for( i = 0; i < 200000UL; ++i )
inp(BasePort + GA_ADDR_OFF);
/* exit mode change state */
outp(WavePort + CD_ADDR_OFF, 0x00);
/* pre-fill the audio buffer with silence */
tmp = size16bit == TRUE ? 0x00 : 0x80;
for( i = 0; i < BUFFERSIZE; ++i )
*(DmaBuffP + i) = (char) tmp;
/* init the "next-buffer" ptr */
NextBuffer = NULL;
#ifdef USE_CODEC_IRQ
/* Write CODEC interrupt count - sample frames per half-buffer (n-1). */
tmp = BUFFERSIZE/2;
if( stereo == TRUE )
tmp >>= 1;
if( size16bit == TRUE )
tmp >>= 1;
CdWrite(CD_LCOUNT_REG, (tmp - 1));
CdWrite(CD_UCOUNT_REG, ((tmp - 1) >> 8));
#endif
/* Setup the PC DMA Controller */
outp(DmacRegP->mask, 0x04 | DmaChan);
outp(DmacRegP->mode, DMAC_AUTO_OUT | DmaChan);
outp(DmacRegP->page, (int) (DmaPaddr >> 16));
/* Disable around 2-byte programming sequences */
_disable();
outp(DmacRegP->clrff, 0x00);
outp(DmacRegP->addr, (int) DmaPaddr);
outp(DmacRegP->addr, (int) DmaPaddr >> 8);
outp(DmacRegP->count, BUFFERSIZE - 1);
outp(DmacRegP->count, (BUFFERSIZE - 1) >> 8);
_enable();
/* clear status, unmask the PC DMA Controller */
inp(DmacRegP->status);
outp(DmacRegP->mask, DmaChan);
/* disable mode change state and start the CoDec */
outp(WavePort + CD_ADDR_OFF, 0x00);
CdWrite(CD_CONFIG_REG, 0x01);
/* flag CODEC-on status and return */
Flags |= FLAG_CODECON;
return 0;
}
/*
char *GetNextBuffer(void);
This function is used by the application, normally in a polling fashion,
to get a pointer to the next buffer half to be filled/stored.
INPUTS:
None
RETURNS:
0 - if the CoDec is not ready for audio data / has no audio data
non-0 - a pointer to a BUFFERSIZE/2 byte buffer will be returned if
the CoDec is ready for data / just filled a buffer
*/
char *GetNextBuffer(void)
{
BYTE *p;
#ifdef USE_CODEC_IRQ
if( (p = NextBuffer) != NULL )
NextBuffer = NULL;
return p;
#else
if( GetDmaCount() < BUFFERSIZE/2 )
p = DmaBuffer;
else
p = DmaBuffer + BUFFERSIZE/2;
if( p == (BYTE)NextBuffer )
return NULL;
NextBuffer = p;
return p;
#endif
}
/*
void PauseCoDec(void);
This function will pause the CoDec auto-restart DMA process.
Don't use this to stop the CoDec - use the StopCoDec function to do
that; it will cleanup DRQs.
INPUTS:
None
RETURNS:
Nothing
*/
void PauseCoDec()
{
CdWrite(CD_CONFIG_REG, CdRead(CD_CONFIG_REG) & 0xfc);
return;
}
/*
void ResumeCoDec(void);
This function will pause the CoDec auto-restart DMA process.
INPUTS:
None
RETURNS:
Nothing
*/
void ResumeCoDec(void)
{
CdWrite(CD_CONFIG_REG, 0x01);
return;
}
/*
void StopCoDec(void);
This function will stop the CoDec auto-restart DMA process.
INPUTS:
None
RETURNS:
Nothing
*/
void StopCoDec(void)
{
WORD i;
CdWrite(CD_CONFIG_REG, CdRead(CD_CONFIG_REG) & 0xfc);
/* Let the CoDec receive its last DACK(s). The DMAC must not be */
/* masked while the CoDec has DRQs pending. */
for( i = 0; i < 64; ++i )
if( !(inp(DmacRegP->status) & (0x10 << DmaChan)) )
break;
#ifdef USE_CODEC_IRQ
;
#else
/* clear internal CODEC interrupt status */
outp(WavePort + CD_STATUS_OFF, 0x00);
#endif
/* unflag CODEC-on status and return */
Flags &= ~FLAG_CODECON;
return;
}
/*
void CloseSoundscape(void);
This function will make sure the CoDec is stopped, return the Soundscape
hardware to it's default Sound Blaster emulation mode, de-install the PCM
interrupt, resore the PIC mask, and do other cleanup.
INPUTS:
None
RETURNS:
None
*/
void CloseSoundscape(void)
{
/* if we're not open, just return */
if( !(Flags & FLAG_OPENED) )
return;
/* if the CoDec is running, stop it */
if( Flags & FLAG_CODECON )
StopCoDec();
/* mask the PC DMA Controller */
outp(DmacRegP->mask, 0x04 | DmaChan);
/* restore all volumes ... */
CdWrite(CD_DACL_REG, DacSavL);
CdWrite(CD_DACR_REG, DacSavR);
CdWrite(CD_CDAUXL_REG, CdxSavL);
CdWrite(CD_CDAUXL_REG, CdxSavR);
/* if necessary, restore gate array resource registers */
GaWrite(GA_INTCFG_REG, IntCfgSav);
GaWrite(GA_DMAB_REG, DmaCfgSav);
GaWrite(GA_CDCFG_REG, CdCfgSav);
#ifdef USE_CODEC_IRQ
/* disable the CoDec interrupt pin */
CdWrite(CD_PINCTL_REG, CdRead(CD_PINCTL_REG) & 0xfd);
/* restore pic mask state */
outp(PicBase + 1, inp(PicBase + 1) | IrqMaskSav);
/* restore the interrupt vector */
_dos_setvect(Vector, VecSav);
#endif
/* unflag open status and return */
Flags &= ~FLAG_OPENED;
return;
}
/*
void SetDacVol(short lvol, short rvol);
This function sets the left and right DAC output level in the CoDec.
INPUTS:
lvol - left volume, 0-127
rvol - right volume, 0-127
RETURNS:
Nothing
*/
void SetDacVol(short lvol, short rvol)
{
CdWrite(CD_DACL_REG, (~lvol & 0x7f) >> 1);
CdWrite(CD_DACR_REG, (~rvol & 0x7f) >> 1);
return;
}
/*
void SetCdRomVol(short lvol, short rvol);
This function sets the left and right CD-ROM output level in the CoDec.
INPUTS:
lvol - left volume, 0-127
rvol - right volume, 0-127
RETURNS:
Nothing
*/
void SetCdRomVol(short lvol, short rvol)
{
CdWrite(CD_CDAUXL_REG, (~lvol & 0x7f) >> 2);
CdWrite(CD_CDAUXR_REG, (~rvol & 0x7f) >> 2);
return;
}
/*
WORD GetDmaCount(void);
This function returns the DMA controller count value for the
CoDec DMA channel.
INPUTS:
None.
RETURNS:
16-bit DMA count value, ffffh to 0000h.
*/
WORD GetDmaCount(void)
{
WORD count0, count1;
_disable();
outp(DmacRegP->clrff, 0x00);
count0 = inp(DmacRegP->count);
count0 |= inp(DmacRegP->count) << 8;
count1 = inp(DmacRegP->count);
count1 |= inp(DmacRegP->count) << 8;
_enable();
if( (BYTE) count1 != (BYTE) count0 )
return count1;
else
return count0;
}
/****************************************************************************
INTERNAL DRIVER FUNCTIONS
****************************************************************************/
/*
short GaRead(WORD rnum);
This function is used to read the indirect addressed registers in the
Ensoniq Soundscape gate array.
INPUTS:
rnum - the numner of the indirect register to be read
RETURNS:
the contents of the indirect register are returned
*/
short GaRead(WORD rnum)
{
outp(BasePort + GA_ADDR_OFF, rnum);
return inp(BasePort + GA_DATA_OFF);
}
/*
short GaWrite(WORD rnum, short value);
This function is used to write the indirect addressed registers in the
Ensoniq Soundscape gate array.
INPUTS:
rnum - the numner of the indirect register to be read
value - the byte value to be written to the indirect register
RETURNS:
Nothing
*/
void GaWrite(WORD rnum, short value)
{
outp(BasePort + GA_ADDR_OFF, rnum);
outp(BasePort + GA_DATA_OFF, value);
return;
}
/*
short CdRead(WORD rnum);
This function is used to read the indirect addressed registers in the
AD-1848 or compatible CODEC. It will preserve the special function bits
in the upper-nibble of the indirect address register.
INPUTS:
rnum - the numner of the indirect register to be read
RETURNS:
the contents of the indirect register are returned
*/
short CdRead(WORD rnum)
{
outp(WavePort + CD_ADDR_OFF,
(inp(WavePort + CD_ADDR_OFF) & 0xf0) | rnum);
return inp(WavePort + CD_DATA_OFF);
}
/*
short CdWrite(WORD rnum, short value);
This function is used to write the indirect addressed registers in the
AD-1848 or compatible CODEC. It will preserve the special function bits
in the upper-nibble of the indirect address register.
INPUTS:
rnum - the numner of the indirect register to be read
value - the byte value to be written to the indirect register
RETURNS:
Nothing
*/
void CdWrite(WORD rnum, short value)
{
outp(WavePort + CD_ADDR_OFF,
(inp(WavePort + CD_ADDR_OFF) & 0xf0) | rnum);
outp(WavePort + CD_DATA_OFF, value);
return;
}
/*
short GetConfigEntry(char *dest, char *entry, FILE *fp);
This function parses a file (SNDSCAPE.INI) for a left-hand string and,
if found, writes its associated right-hand value to a destination buffer.
This function is case-insensitive.
INPUTS:
fp - a file pointer to the open SNDSCAPE.INI config file
dst - the destination buffer pointer
lhp - a pointer to the right-hand string
RETURNS:
0 - if successful
-1 - if the right-hand string is not found or has no equate
*/
short GetConfigEntry(char *dest, char *entry, FILE *fp)
{
char static str[83];
char static tokstr[33];
char *p;
/* make a local copy of the entry, upper-case it */
strcpy(tokstr, entry);
strupr(tokstr);
/* rewind the file and try to find it ... */
rewind(fp);
for( ;; )
{
/* get the next string from the file */
fgets(str, 83, fp);
if( feof(fp) )
return -1;
/* properly terminate the string */
for( p = str; *p != '\0'; ++p )
{
if( *p == ' ' || *p == '\t' || *p == 0x0a || *p == 0x0d )
{
*p = '\0';
break;
}
}
/* see if it's an 'equate' string; if so, zero the '=' */
if( !(p = strchr(str, '=')) )
continue;
*p = '\0';
/* upper-case the current string and test it */
strupr(str);
if( strcmp(str, tokstr) )
continue;
/* it's our string - copy the right-hand value to buffer */
for( p = str + strlen(str) + 1; (*dest++ = *p++) != '\0'; )
;
break;
}
return 0;
}
#ifdef USE_CODEC_IRQ
/*
The Wave ISR
This ISR sets the global next-buffer pointer based on the DMA controller
count at the time of the interrupt.
*/
void interrupt WaveIsr()
{
/* verify PIC in-service reg for IRQs 7 and 15 */
if( (WaveIrq & 0x07) == 0x07 )
{
outp(PicBase, 0x0b);
inp(PicBase);
if( !(inp(PicBase) & 0x80) )
return;
}
/* if the CoDec is interrupting ... */
if( inp(WavePort + CD_STATUS_OFF) & 0x01 )
{
/* set the next-buffer to the next buffer half to fill */
if( GetDmaCount() < BUFFERSIZE/2 )
NextBuffer = DmaBuffer;
else
NextBuffer = DmaBuffer + BUFFERSIZE/2;
/* clear the CODEC interrupt */
outp(WavePort + CD_STATUS_OFF, 0x00);
}
/* clear the PC interrupt controller */
if(WaveIrq >= 8)
outp(0xa0, 0x20);
outp(0x20, 0x20);
return;
}
#endif
--------------7E1E274D67D--
- Raw text -