521 lines
24 KiB
Plaintext
521 lines
24 KiB
Plaintext
|
Newsgroups: alt.2600
|
||
|
Path: sundog.tiac.net!europa.eng.gtefsd.com!emory!swrinde!pipex!sunic!EU.net!uunet!iat.holonet.net!clafave
|
||
|
From: clafave@iat.holonet.net (Christopher R LaFave)
|
||
|
Subject: Re: Hacking aTM
|
||
|
Message-ID: <CsCvt5.Lut@iat.holonet.net>
|
||
|
Organization: HoloNet National Internet Access System: 510-704-1058/modem
|
||
|
References: <2v5ev9$53p@lucy.infi.net>
|
||
|
Date: Sun, 3 Jul 1994 08:34:16 GMT
|
||
|
Lines: 510
|
||
|
|
||
|
Magnetic stripe technology
|
||
|
|
||
|
|
||
|
Figures and listings are at the end.
|
||
|
|
||
|
Because of their widespread use, most magnetic cards adhere to well-defined
|
||
|
standards that describe the physical and magnetic characteristics for a
|
||
|
magnetic stripe on a plastic card. These standards outline specifications for a
|
||
|
storage format and information interchange. This does not preclude other
|
||
|
encoding techniques or additional data tracks for specific applications, but in
|
||
|
most cases it makes sense to adhere to at least the basic constraints. This
|
||
|
gives you the choice of using any commercially available magnetic encoders for
|
||
|
your application. The technique used for encoding magnetic cards is known as
|
||
|
"Two-Frequency, Coherent Phase Recording". Allowing for the representation of
|
||
|
single-channel, self-clocking serial data, this methodology is generally
|
||
|
referred to as F/2F. Self-clocking is achieved by combining data and clock bits
|
||
|
together in a continuous, synchronous sequence. In this scheme, an intermediate
|
||
|
flux transition signifies a one bit and the absence of an intermediate flux
|
||
|
transition denotes a zero bit.
|
||
|
Three data tracks defined for use on standard magnetic cards each possess
|
||
|
different bit densities and encoded character sets. The average bit density of
|
||
|
track 1 is 210 bits per inch (bpi). Track 1 characters are made up of six data
|
||
|
bits and an odd parity bit, encoded with the least-significant bit first and
|
||
|
the parity bit last, yielding a 64-character set. Taking the number of bits per
|
||
|
inch and the number of bits per character, you can see track 1 has the capacity
|
||
|
to hold 79 characters.
|
||
|
Track 2 has a bit density of 75 bpi, and track 3 uses 210 bpi. Both of these
|
||
|
tracks allow the representation of a numeric-only character set. The characters
|
||
|
for tracks 2 and 3 are encoded using a 4-bit binary-coded decimal subset with
|
||
|
odd parity and, like track 1, are encoded with the least significant bit first
|
||
|
and the parity bit last. The lower density of track 2 allows up to 40 numeric
|
||
|
characters, where 107 numerics can be squeezed onto track 3. The actual number
|
||
|
of usable characters will be fewer since you also have the Start Sentinel, End
|
||
|
Sentinel, and LRC characters.
|
||
|
Though sometimes magnetic cards are moved past the read head mechanically,
|
||
|
most applications rely on manually moving the card, either through a slotted
|
||
|
reader or into an insertion-type reader. Typically the swipe rate is 5-20 inches
|
||
|
per second (ips), with 50 ips being the fastest most card readers can handle. Of
|
||
|
course, moving the card by hand will not only result in varying the absolute
|
||
|
card velocity but, will also introduce incremental speed changes as the card
|
||
|
accelerates and decelerates past the pickup. The F/2F scheme is very forgiving
|
||
|
of such speed fluctuations.
|
||
|
For all 3 tracks, the fundamental data format is similar and consists of the
|
||
|
following elements: First, leading zero bits are encoded to indicate the
|
||
|
presence of an encoded magnetic card and provide synchronization pulses to the
|
||
|
read head electronics and ultimately to the controller. Next, the Start Sentinel
|
||
|
character is encoded which indicates the start of the actual data. The coded
|
||
|
data follows. Next, the End Sentinel terminates the data portion of the card
|
||
|
and followed by an LRC byte (used for error detection). The LRC is essentially
|
||
|
a horizontal parity calculated by an exclusive-OR of all the data bits from the
|
||
|
Start Sentinel to the End Sentinel (inclusive). Finally, trailing zeros follow
|
||
|
the LRC and fill out the remainder of the card.
|
||
|
ANATOMY OF A MAGNETIC CARD
|
||
|
The magnetic tracks have inherent characteristics based on details such as
|
||
|
code set and bit and character densities. International organizations such as
|
||
|
Mastercard and VISA impose additional constraits for their participating members
|
||
|
and standards exist for bank debit cards and ATM cards as well. These rules
|
||
|
specify the exact content and format of each data field in aech track as well as
|
||
|
the intended uses for the tracks. Naturally, for nonfinancial uses, it is not
|
||
|
necessary to comply with these standards. For dedicated uses such as access
|
||
|
control, people tracking, and material tracking, adhering to the minimal
|
||
|
standards is adequate.
|
||
|
The most-often-used track is track 2, although it offers the lowest inform-
|
||
|
ation density of the three. It contains all the information that is normally
|
||
|
used for credit card transactions. When a customer name is required, track 1
|
||
|
must be used since it is the only track that permits alphanumeric data. Track 3
|
||
|
is specified for numeric-only data, but is unique. It is intended for change-
|
||
|
able data and consequently may not only be read but may be rewritten by the
|
||
|
transaction-handling equipment. A multitude of data fileds are contained in
|
||
|
these various tracks. Figure 1 shows a brief run-down of what is generally
|
||
|
placed on tracks 1 and 2.
|
||
|
REAL CARD READERS
|
||
|
The recovery of magnetically encoded F/2F data can be accomplished directly
|
||
|
with the use of just about any microcontroller. There are no particular
|
||
|
difficulties in deciphering the raw F/2F data stream and many early magnetic
|
||
|
read heads contained nothing more than signal amplifiers and line drivers.
|
||
|
These are now artifacts since all modern magnetic read heads contain integ-
|
||
|
rated F/2F bit recovery circuitry and interface with the host controller in a
|
||
|
standard fashion using three wires: CARD PRESENT, CLOCK, and DATA. The read
|
||
|
heads usually rely on a single chip to perform the linear signal conditioning,
|
||
|
sychronization, and recovery of individual bits from the data stream. The Mag-
|
||
|
Tek 21006505 IC is representative of this type of data recovery circuit and its
|
||
|
functionality is depicted in figure 2.
|
||
|
Linear conditioning consists of raising the level of the magnetic pickup's
|
||
|
input signal, rejecting common-mode noise, conditioning and detecting the
|
||
|
signal, and finally providing a digital output for susequent processing. The
|
||
|
enable/disable counters provide initialization for the recovery section. The
|
||
|
recovery section locks onto the data rate and recovers the individual data bits
|
||
|
from the data stream. The oscillator section provides the clocks for the recov-
|
||
|
ery section and for the enable/disable timers. Card present goes low after 8 or
|
||
|
9 flux reversals are seen from the magnetic pickup and will return high about
|
||
|
50 ms after the last flux reversal. The strobe line signals that data is valid
|
||
|
and is active low. The data pin indicates a one bit when it is low. Raw F/2F
|
||
|
data can also be picked directly off the chip.
|
||
|
The data rate for a high-density track scanned at 50 ips comes to 10500 bits
|
||
|
per second (bps). This results in a transfer rate of 1500 characters per second
|
||
|
for the 7-bit elements used on track 1, and 2100 characters per second using
|
||
|
the 5-bit elements of track 3. I either case, this translates to a new bit
|
||
|
arriving at the controller just under every 100 us (microsecond). Even the most
|
||
|
anemic controller should be able to keep up. With resonably good coding techni-
|
||
|
ques, there should be no problem handling the entire data sampling phase on an
|
||
|
interrupt-driven basis. The low-density (track 2) data flows at a more pedes-
|
||
|
trian 3750 bps, yielding 750 5-bit characters per second, or a new bit every
|
||
|
266 us. Since most dual-track read heads provide track 1 and track 2 data, this
|
||
|
indicates that handling both tracks simultaneously is feasible under interrupt
|
||
|
control. Keep in mind that 50 ips is a rather fast scan rate; 20-30 ips is
|
||
|
probably a more realistic limit.
|
||
|
MAGNETIC BIT STORMS
|
||
|
When approaching a problem such as decoding magnetic cards, it pays to spend
|
||
|
some time looking at the overall picture before starting to write the code. At
|
||
|
first glance, it would seem that organizing the data into the prevailing element
|
||
|
size during the sampling interval would make decoding easier. This could be
|
||
|
easily done by ignoring all the leading zeros, with actual data storage comm-
|
||
|
encing with the first one bit. Of course, this approach assumes you're getting
|
||
|
good data. The fact that the data recovery is handled using well-proven hard-
|
||
|
ware makes this assumption valid.
|
||
|
If all you need to do is decode the card in a forward direction, then going
|
||
|
about things as I just described makes sense and reduces the coding effort to a
|
||
|
trivial exercise. If you have to support reverse decoding then this is not the
|
||
|
optimal solution. Having considered the tradeoffs of being able to decode a
|
||
|
magnetic card in both forward and reverse directions, I decided to structure the
|
||
|
program to work equally well in either direction at the cost of a slight
|
||
|
increase in initial complexity.
|
||
|
The first step in decoding is to acquire the serial bit stream. This can be
|
||
|
done using a dedicated sample loop or, with a little more work, using interrupt
|
||
|
processing. Since the idea is the same regardless of the details, I decided to
|
||
|
use a sample loop in my demonstration program (listing 1). The code simply
|
||
|
records the incoming data stream and deposits the data in a sample buffer a byte
|
||
|
at a time. Sampling begins when Card Present returns idle. Any incomplete byte
|
||
|
that has been partially assembled at the time when sampling terminates is simply
|
||
|
discarded. The abundance of leading and trailing zeros allows losing some bits
|
||
|
at either end causing any problems.
|
||
|
Once the sampling interval completes, control is transferred to the decoding
|
||
|
algorithm. Presented in listing 2, the track-1 decode algorithm consists of
|
||
|
nothing more than some initialization and the essence of the decode logic.
|
||
|
Limiting the gyrations contained in the main body of this routine not only makes
|
||
|
the logic easy to follow, but permits the same code to handle the decoding in
|
||
|
either a forward or reverse direction.
|
||
|
The initial entry point assumes a forward decode attempt and sets up the
|
||
|
necessary flags, pointers, and counters before jumping into the main initial-
|
||
|
ization code. After initialization, the sample buffer is scanned for the first
|
||
|
one bit, at which time a 7-bit element is assembled. If the parity is correct
|
||
|
and the character code checks out to be a Start Sentinel, the code proceeds and
|
||
|
starts pulling successive data elements from the sample buffer. If the data
|
||
|
element is not an End Sentinel, the character is translated to ASCII and stored
|
||
|
in the decode buffer. Should an End Sentinel be detected, the program extracts
|
||
|
the next character, which is assumed to be the LRC byte, and finally checks the
|
||
|
calculated LRC for a value of zero.
|
||
|
The checks and balances included in the execution of this loop include things
|
||
|
such as parity, a cumulative LRC, and checking to make sure I haven't run out of
|
||
|
samples. If everything checks out, the program terminates and returns with the
|
||
|
DPTR pointing to the decoded data buffer and the character count contained in
|
||
|
ACC. Should a decode failure occur, a test is performed on the direction flag
|
||
|
and if this is an attempt at a forward decode, the routine jumps to the reverse
|
||
|
initialization entry point. The reverse entry is similar to the forward decode
|
||
|
entry but sets up the sample pointer to the end of the sample buffer and sets
|
||
|
the direction flag to indicate a reverse operation.
|
||
|
The routines contained in the intermediate layer are shown in listing 3. The
|
||
|
meaning and operation of these routines should be apparent. The key routine in
|
||
|
this section is GET_BIT, which picks off the next bit from the sample buffer,
|
||
|
essentially restoring the sequential nature of the initial magnetic bit stream.
|
||
|
FIND_START is used to synchronize with the first one bit. GET_CHAR first checks
|
||
|
to make sure it hasn't run out of samples, then assembles the next 7-bit data
|
||
|
element while doing a parity test and LRC calculation. Any problems encountered
|
||
|
here are sent back to the caller and are handled there. STORE_CHAR translates
|
||
|
and deposits the data character into its respective location in the decoded-data
|
||
|
buffer and increments the character counter.
|
||
|
Listing 4 shows the low-level code. These routines perform the most rudiment-
|
||
|
ary functions and operate in accordance with the direction flag. INDEX_PTR
|
||
|
either increments or decrements the sample pointer, POSITION_BIT likewise does
|
||
|
either a right or left shift and LOCATE_BIT returns the state of the least- or
|
||
|
most- significant bit of the accumulator.
|
||
|
GO AHEAD, TAKE A SWIPE
|
||
|
Let me touch on a few additional points that may not be immediately apparent
|
||
|
before signing off. Storing the sampled data in a continuous stream makes the
|
||
|
sample routine work equally well with the various bit configurations used for
|
||
|
the different recording tracks. This would not be easily attained if you tried
|
||
|
to generate a particular element format during sample time. Furthermore, if you
|
||
|
look at the differences between the encoded character sets and the bit formats
|
||
|
for the different tracks, you will find that they differ in only a few areas.
|
||
|
With a few minor changes, such as the defined Start Sentinel, number of bits per
|
||
|
element, and character translation method, the decode routine I've shown could
|
||
|
easily be coerced to handle the decoding of any of the standard magnetic tracks.
|
||
|
As a matter of fact, by recoding and redefining the hard-coded constants as
|
||
|
variables, these could be set up for the particular data track at execution
|
||
|
time before invoking the decode function. Doing so would not only save program
|
||
|
memory, but would also allow you to use a routine you were comfortable with.
|
||
|
|
||
|
*******************************************************************************
|
||
|
FIGURE 1
|
||
|
_____________________________________________________________________
|
||
|
TRACK 1 |SS|FC| PAN |FS| NAME |FS| ADDITIONAL DATA |ES|LRC|
|
||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
Notes: Track 1 is limited to 79 characters including SS, ES, and LRC.
|
||
|
Mastercard PAN is variable up to 16 characters maximum. VISA is 13 or 16
|
||
|
characters, including mod-10 check digit.
|
||
|
SS: Start sentinel (%)
|
||
|
FC: Format code
|
||
|
FS: Field separator ({)
|
||
|
ES: End sentinel (?)
|
||
|
LRC: Longitudinal redundency check character
|
||
|
PAN (primary account number): 19 digits max.
|
||
|
NAME: 26 alphanumeric characters max.
|
||
|
ADDITIONAL DATA: Expiration date 4
|
||
|
Restriction or type 3
|
||
|
Offset or PVN 5
|
||
|
______________________________________________________________________
|
||
|
TRACK 2 |SS|FC| PAN |FS| ADDITIONAL DATA |ES|LRC|
|
||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
SS: Start sentinel (;) (hex B)
|
||
|
FS: Field separator (=) (hex D)
|
||
|
ES: End sentinel (?) (hex F)
|
||
|
LRC: Longitudinal redundency check character
|
||
|
PAN (primary account number): 19 digits max.
|
||
|
ADDITIONAL DATA: Expiration date 4
|
||
|
Restriction or type 3
|
||
|
Offset or PVN 5
|
||
|
|
||
|
*****************************************************************************
|
||
|
FIGURE 2
|
||
|
HEAD SIGNAL __/~\__ __/~\__ __/~\__ __/~\__ __/~\__ __/~\_______
|
||
|
`-` `-` `-` `-` `-`
|
||
|
____ _______ __________ ____
|
||
|
CARD PRESENT |______________________________________________|
|
||
|
|
||
|
____ ____ _____________________ ____ ____ _____
|
||
|
READ DATA |____| |_________| |_________|
|
||
|
|
||
|
____ ______ __________ __ __ __ __ __ __ __ __ __ __
|
||
|
READ STROBE |__| |_| |_| |_| |_| |_| |_| |_| |_| |_| |
|
||
|
|
||
|
| 0| 1| 0| 1| 1| 0| 1| 1| 0| 0|
|
||
|
|
||
|
|
||
|
___
|
||
|
HEAD 1-----X---X--------13 |MAG| 9--C2-R3
|
||
|
R1 C1 |TEK| |
|
||
|
HEAD 2-----X---X--------12 | | 10-----X--X
|
||
|
| | | |
|
||
|
X---X------11 | | | R5
|
||
|
X-X R2 C6 | | 6------|--X
|
||
|
| | X---X-------8 | | |
|
||
|
+5VDC --------------X--R7--X--------X-|-----------------14 | | 7-----C3
|
||
|
| |+ | | | ____
|
||
|
R6 C5 | | | 16----DATA
|
||
|
| X----------|------------------5 | | ______
|
||
|
____ _______ | | X------------------2 | | 15----STROBE
|
||
|
CARD PRESENT -------X------|-----------------------------1 | |
|
||
|
| X----X-----3 | |
|
||
|
| C4 R4----4 | |
|
||
|
GND------------------------X------------------X |___|
|
||
|
|
||
|
R=RESISTOR(Values weren't given)
|
||
|
C=CAPACITOR(Values weren't given)
|
||
|
X=CONNECTION BETWEEN WIRES
|
||
|
MAG TEK is the Mag-Tek 21006505
|
||
|
|
||
|
*****************************************************************************
|
||
|
LISTING 1-- Using several externally defined routines, a sample program to
|
||
|
read a stripe and store it in a buffer is very short.
|
||
|
|
||
|
PUBLIC READ_MAG1
|
||
|
EXTERN MS1_BUF (XDATA) ;sample buffer
|
||
|
EXTERN MS1_LIM (NUMBER) ;sample limit
|
||
|
EXTERN MD1_BUF (XDATA) ;decode buffer
|
||
|
EXTERN CP (BIT) ;card present bit
|
||
|
EXTERN M1_CLK (BIT) ;clock bit
|
||
|
EXTERN M1_DQ (BIT) ;data bit
|
||
|
|
||
|
M1_SS EQU 5 ;start sentinel
|
||
|
M1_ES EQU 1FH ;end sentinel
|
||
|
;
|
||
|
SEG CODE
|
||
|
;Sample and decode magnetic track 1
|
||
|
; output: ACC contains character count.
|
||
|
; DPTR points to data buffer
|
||
|
READ_MAG1 PROC
|
||
|
CALL MAG_SAMPLE
|
||
|
JZ L?RM1
|
||
|
CALL MAG1_DECODE
|
||
|
L?RM1: RET
|
||
|
ENDPROC
|
||
|
;General-purpose magnetic sampling routine
|
||
|
; output: ACC contains sample count
|
||
|
MAG_SAMPLE PROC
|
||
|
MOV DPTR,#MS1_BUF
|
||
|
MOV R1,#0 ;sample counter
|
||
|
L?MS1: MOV R0,#8 ;bit counter
|
||
|
L?MS2: JB CP,L?MS4
|
||
|
JB M1_CLK,L?MS2
|
||
|
MOV C,M1_DQ
|
||
|
L?MS3: JB CP,L?MS4
|
||
|
JNB M1_CLK,L?MS3
|
||
|
CPL C
|
||
|
RRC A
|
||
|
DJNZ R0,L?MS2
|
||
|
MOVX @DPTR,A
|
||
|
INC DPTR
|
||
|
INC R1 ;sample counter
|
||
|
CJNE R1,#MS1_LIM,L?MS1
|
||
|
L?MS4: MOV A,R1 ;final sample count
|
||
|
RET
|
||
|
ENDPROC
|
||
|
|
||
|
*******************************************************************************
|
||
|
LISTING 2-- Once the serial bit stream has been acquired, the decoding
|
||
|
algorithm takes over.
|
||
|
|
||
|
;Bidirectional magnetic track 1 decode routine
|
||
|
;input: sample count in ACC, output: ACC contains character count
|
||
|
; DPTR points to decoded data buffer
|
||
|
;Reg. usage for this routine (includes subroutines) as follows:
|
||
|
;R5: Direction flag, 0=forward
|
||
|
;R4: Cumulative LRC register
|
||
|
;R3: Decoded character counter
|
||
|
;R2: Nondecrementing sample count
|
||
|
;R1: Decrementing sample count
|
||
|
;R0: Bit syncronizing counter
|
||
|
MAG1_DECODE PROC
|
||
|
;1st pass, setup for forward decode attempt
|
||
|
MOV R5,#0 ;indicate forward decode
|
||
|
MOV DPTR,#MS1_BUF ;point to start of buffer
|
||
|
MOV R1,A ;sample counter
|
||
|
MOV R2,A ;sample count
|
||
|
JMP L?M1D1
|
||
|
L?M1D0: ;2nd pass, setup for reverse decode attempt
|
||
|
MOV R5,#1 ;indicate reverse decode
|
||
|
MOV DPTR,#MS1_BUF
|
||
|
MOV A,R2 ;sample count
|
||
|
DEC A
|
||
|
ADD A,DPL
|
||
|
MOV DPL,A
|
||
|
CLR A
|
||
|
ADDC A,DPH
|
||
|
MOV DPH,A ;point to end of buffer
|
||
|
MOV R1,2 ;sample counter
|
||
|
L?M1D1: ;decode initialization
|
||
|
MOV R3,#0 ;character counter
|
||
|
MOV R4,#0 ;inital LRC
|
||
|
MOV R0,#0 ;bit syncronizer
|
||
|
CALL FIND_START ;main decode loop
|
||
|
JNC L?M1D5 ;start bit error
|
||
|
CALL GET_CHAR ;Start Sentinel
|
||
|
JB ACC.7,L?M1D5 ;format error
|
||
|
CJNE A,#M1_SS,L?M1D5 ;start setinel error
|
||
|
L?M1D2: ;data byte or End Sentinel
|
||
|
CALL GET_CHAR
|
||
|
JB ACC.7,L?M1D5 ;format error
|
||
|
CJNE A,#M1_ES,L?M1D3 ;end sentinel not found
|
||
|
JMP L?M1D4
|
||
|
L?M1D3:
|
||
|
CALL STORE_CHAR ;data character
|
||
|
JMP L?M1D2
|
||
|
L?M1D4: ;LRC
|
||
|
CALL GET_CHAR ;get LRC
|
||
|
JB ACC.7,L?M1D5 ;format error
|
||
|
MOV A,R4
|
||
|
JNZ L?M1D5 ;LRC error
|
||
|
MOV DPTR,#MD1_BUF ;good return
|
||
|
MOV A,R3 ;final character
|
||
|
RET
|
||
|
L?M1D5: ;decode error, check if 1st pass
|
||
|
CJNE R5,#1,L?M1D0 ;check direction
|
||
|
CLR A ;bad return
|
||
|
RET
|
||
|
ENDPROC
|
||
|
|
||
|
*******************************************************************************
|
||
|
LISTING 3-- The intermediate layer of software is one level removed from the
|
||
|
nitty-gritty details.
|
||
|
|
||
|
;Get the next bit from the sample buffer
|
||
|
;output: C contains data bit
|
||
|
GET_BIT PROC
|
||
|
CJNE R0,#0,L?GB1 ;bit synchronizer
|
||
|
MOV R0,#8
|
||
|
PUSH ACC
|
||
|
MOVX A,@DPTR
|
||
|
CALL INDEX_PTR
|
||
|
MOV B,A
|
||
|
POP ACC
|
||
|
DEC R1 ;sample counter
|
||
|
L?GB1: XCH A,B
|
||
|
CALL POSITION_BIT
|
||
|
XCH A,B
|
||
|
DEC R0 ;bit synchronizer
|
||
|
RET
|
||
|
ENDPROC
|
||
|
;
|
||
|
;Find the first '1' bit in the sample buffer
|
||
|
;output: C=1 if bit is found
|
||
|
FIND_START PROC
|
||
|
L?FS1:
|
||
|
CJNE R0,#0,L?FS2 ;bit synchronizer
|
||
|
MOV R0,#8
|
||
|
MOVX A,@DPTR
|
||
|
CALL INDEX_PTR
|
||
|
DJNZ R1,L?FS2 ;sample counter
|
||
|
JMP L?FS4 ;out of samples
|
||
|
L?FS2: CALL LOCATE_BIT ;test for a '1' bit
|
||
|
JC L?FS3
|
||
|
CALL POSITION_BIT
|
||
|
DEC R0 ;bit synchronizer
|
||
|
JMP L?FS1
|
||
|
L?FS3: ;good return
|
||
|
MOV B,A ;save copy in B
|
||
|
SETB C
|
||
|
RET
|
||
|
L?FS4: CLR C ;bad return
|
||
|
RET
|
||
|
ENDPROC
|
||
|
;
|
||
|
;Get the next 7 bit element from the sample buffer
|
||
|
;output: ACC contains data element
|
||
|
; error flag is ACC.7
|
||
|
GET_CHAR PROC
|
||
|
MOV A,R1 ;sample counter
|
||
|
JZ L?GC2 ;out of samples
|
||
|
MOV R7,#7 ;bit counter
|
||
|
CLR A
|
||
|
L?GC1: CALL GET_BIT ;next bit
|
||
|
RRC A
|
||
|
DJNZ R7,L?GC1
|
||
|
RR A
|
||
|
JNB P,L?GC2 ;parity error
|
||
|
ANL A,#3FH ;discard parity
|
||
|
PUSH ACC
|
||
|
XRL A,R4 ;calculate LRC
|
||
|
MOV R4,A
|
||
|
POP ACC ;good return
|
||
|
RET
|
||
|
L?GC2: SETB ACC.7 ;bad return
|
||
|
RET
|
||
|
ENDPROC
|
||
|
;Translate and store the data character
|
||
|
;input: ACC contains data character
|
||
|
STORE_CHAR PROC
|
||
|
PUSH DPL
|
||
|
PUSH DPH
|
||
|
PUSH ACC
|
||
|
MOV DPTR,#MD1_BUF
|
||
|
MOV A,R3 ;character counter
|
||
|
ADD A,DPL
|
||
|
MOV DPL,A
|
||
|
CLR A
|
||
|
ADDC A,DPH ;generate displacement
|
||
|
MOV DPH,A
|
||
|
POP ACC
|
||
|
ADD A,#' ' ;translate
|
||
|
MOVX @DPTR,A ;store
|
||
|
POP DPH
|
||
|
POP DPL
|
||
|
INC R3 ;character counter
|
||
|
RET
|
||
|
ENDPROC
|
||
|
|
||
|
*******************************************************************************
|
||
|
LISTING 4-- The low-level routines get right down to the ground and take care
|
||
|
of the gory details.
|
||
|
|
||
|
;Index the sample pointer either forward or backward
|
||
|
INDEX_PTR PROC
|
||
|
CJNE R5,#0,L?IP1 ;check direction
|
||
|
INC DPTR ;forward
|
||
|
RET
|
||
|
L?IP1: PUSH ACC ;backward
|
||
|
DEC DPL
|
||
|
MOV A,DPL
|
||
|
CJNE A,#-1,L?IP2
|
||
|
DEC DPH
|
||
|
L?IP2: POP ACC
|
||
|
RET
|
||
|
ENDPROC
|
||
|
|
||
|
;Position bit is in ACC into C in either a right or left shift
|
||
|
POSITION_BIT PROC
|
||
|
CJNE R5,#0,L?PB1 ;check direction
|
||
|
RRC A ;forward
|
||
|
RET
|
||
|
L?PB1: RLC A
|
||
|
RET
|
||
|
ENDPROC
|
||
|
|
||
|
;Locate a 1 bit, either msb or lsb
|
||
|
;output: C=1 if bit is a one
|
||
|
LOCATE_BIT PROC
|
||
|
CJNE R5,#0,L?LB1 ;check direction
|
||
|
MOV C,ACC.0 ;forward
|
||
|
RET
|
||
|
L?LB1: MOV C,ACC.7 ;backward
|
||
|
RET
|
||
|
ENDPROC
|
||
|
|
||
|
*******************************************************************************
|
||
|
Contact:
|
||
|
Mag-Tek, Inc.
|
||
|
20725 S. Annalee Ave.
|
||
|
Carson, CA 90746
|
||
|
(213) 631-8602
|
||
|
fax: (213) 631-3956
|
||
|
*******************************************************************************
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
--
|
||
|
_
|
||
|
__ | __ | ` __ __ clafave@holonet.net
|
||
|
| | __| ~|~ __|| ||__| Beaverton, Oregon USA
|
||
|
|__ |_ |__| | |__| \/ |__. GO BLAZERS!
|