TweetFollow Us on Twitter

MacLarry

Volume Number: 16 (2000)
Issue Number: 2
Column Tag: Programming

MacLarry

By F.C. Kuechmann, Vancouver WA

An M68HC11 S-record dissecting kit in CodeWarrior Pascal

Introduction

S-record files are used to transfer code from development systems to PROM and EPROM programmers. This article discusses the implementation and use of MacLarry, an S-record dissecting program. MacLarry allows you to view the contents of S-records in several formats.

What is an S-Record File?

Programmers who are confined to the desktop world experience code movement in terms of disk file to memory and back to disk. They are often unfamiliar with hex-format files that are used to transfer program code from a desktop development system to an EPROM (Erasable Programmable Read-Only Memory) or PROM programmer for use in an embedded controller. Common examples of embedded controllers include the M68HC16 in a Jaz drive, and the M683xx-series inhabiting Palm Pilot PDAs and anti-lock brake systems (ABSes) in General Motors cars. In each of these cases the program code probably spent some time in S-records.

How do we use one?

In a typical instance you might be writing a BASIC11 program for a Micromint RTC-HC11 stackable module to be used to control a gate and barrier system at a fish hatchery. After the usual multiple-pass write-debug-modify-debug cycle you send an "ESAVE" command to the RTC-HC11 via the RS-232C serial port connection (usually the modem port on a Mac). The RTC-HC11, after a brief delay, churns out the program in its memory in the form of S-records. Using a terminal program you capture the records to a text file, clean up any garbage with an editor, and transfer the file to an EPROM programmer via a serial port. When the file has been written to a 27256 (32k-by-8) EPROM by the programmer, you type an "AUTOST" command to the RTC-HC11, shut off its power, and replace the resident EPROM with the one you've just programmed. Restore the power and the program autostarts and runs.

At another time you might submit the MC68HC05b6 code for your smart toaster to Motorola in the form of an S-record file so that the processors for your next production run can be masked with the code during manufacture.

How is MacLarry Useful?

In the normal course of embedded development MacLarry is probably unnecessary. You already have the assembler sourcecode. But what if old friend Murphy (of Murphy's Law fame) visits and destroys all copies of your source, leaving you with only an S-record file of your code? MacLarry could be very useful in helping recover your source.

Perhaps, after getting so involved in programming and testing that you neglected to clearly label the files, you're merely trying to determine which S-record file holds the most recent version, or a particular version, of your code.

Or what if you're just curious about some S-record files you found and downloaded from the net? MacLarry again comes to the rescue.

What does an S-record look like?

The S-record format originated at Motorola and is commonly used with code for processors made by that company, including the extremely popular MC68HC05 used in cellular phones and pagers, and the M68HC11 series. S-records are similar in concept to the more common Intel hex format with a few important differences in detail. An S-record line consists of a two-character header, a two-character hex bytecount that gives the number of bytes in the record, a two-byte hex address, bytecount minus three hex bytes of code, and a one's complement hex checksum. The S-record files that MacLarry deals with have "S1" headers on all lines but the last, which has an "S9" header and no significant content. The components of a line with an "S1" header might look like Figure 1.


Figure 1. S-record format.

"S1" is the header. The next two digits (13) indicate in hex the number of bytes to follow in the line, including a four-digit address and two-digit checksum. The "0000" is the address and is followed by 10 (hex, 16 decimal) bytes of data and the "BF" checksum.

MacLarry the Dissector

The CodeWarrior Pascal program MacLarry can be used to dissect M68HC11 S-records in order to recreate assembler sourcecode. MacLarry dissects 68HC11 S-records three ways:

  • Asciification - isolates printable ASCII characters (range 32-126).
  • Disassembly into mnemonics with address and data fields.
  • Disassembly without address and data fields for ready conversion to assembler sourcecode.

Asciification or dumpster?

What is "asciification"? This method of dissecting S-record files prints characters in the ASCII range 32-126 beside the address and hex data, replacing characters outside that range with dots. This sort of output is commonly called a "hex dump" when derived from a binary file. But what is it if you start with a hex file to begin with? Can you hex dump a hex file? "Dumpster" might be a more descriptive name than "asciifier". Whatever it's called, it simplifies locating embedded text strings and command tables in the target file.

The output files

A MacLarry dissection creates three files, one each with the extensions "asc", "dis" and "src" appended to the original filename minus the usual "S19" extension. Thus if the S-record filename is JudithWhatley.S19, the output files will be JudithWhatley.asc, JudithWhatley.dis and JudithWhatley.src.

Sample output

Listing 1a shows the asciification of parts of the file BASIC113.S19, containing version 1.13 of BASIC11. BASIC11 is a freeware integer BASIC interpreter for the M68HC11. Versions of this BASIC are distributed by companies such as New Micros Inc, and MicroMint Inc with some of their M68HC11-based stackable control computers. Generic versions that aren't modified specifically for a given manufacturer's boards are also freely available via the internet. Some URLs can be found at the end of this article. The listing shows the identity text and copyright notice.


Listing 1a.

Ascifying bas113.s19
Start     19:38:41
Finish    19:39:08

 E000 0000   7EED30BDECD6B6600881552603BDF116  ~.0....'..U&....
 E010 0010   BDE0608EDFFF0E4F5FDD3ADD3FDD41BD  ..'....O_.:.?.A.
 E020 0020   E0A37F00197F00187F001DBDE0F4BDE0  ................
 E030 0030   B3BDE053BDEE0F1A83000026D6DE0EDF  ...S.......&....
 E040 0040   2FBDE1167D001926CA20D7DE2F6F00DF  /...}..&. ../o..
 E050 0050   0E20C0BDE38C81202605BDE39520F439  . ..... &.... .9
 E060 0060   CEE0667EE10B0D0A0D0A424153494331  ..f~......BASIC1
 E070 0070   312076312E31330D0A436F7079726967  1 v1.13..Copyrig
 E080 0080   687420313938352C313938362062790D  ht 1985,1986 by.
 E090 0090   0A476F72646F6E20446F7567686D616E  .Gordon Doughman
 E0A0 00A0   0D0A00CEE0A97EE10B0D0A5245414459  ......~....READY

Listing 1b reveals a table of BASIC11 commands, which are presumably used in tokenizing input.

Listing 1b.

 E200 0200   813A2607867ABDE3A020B3810D270586  .:&..z... ...'..
 E210 0210   027EEA4E867DBDE3A0DC0283DE00CEDE  .~.N.}..........
 E220 0220   00E7023944415441000CE3BC4C455400  ...9DATA....LET.
 E230 0230   01E3DC52454144000DEA0E524553544F  ...READ....RESTO
 E240 0240   5245000EE3BB474F535542000FE3B247  RE....GOSUB....G
 E250 0250   4F544F0012E3B24F4E54494D450025E8  OTO....ONTIME.%.
 E260 0260   894F4E4952510026E8894F4E50414343  .ONIRQ.&..ONPACC
 E270 0270   0028E8A94F4E0013E83B52455455524E  .(..ON...;RETURN
 E280 0280   0014E3BB49460015E8BD494E50555400  ....IF....INPUT.
 E290 0290   16E9F65052494E540003E98E3F0003E9  ...PRINT....?...
 E2A0 02A0   8E464F520004E9264E4558540005E981  .FOR...&NEXT....
 E2B0 02B0   53544F500017E3BB454E445748001AE3  STOP....ENDWH...
 E2C0 02C0   BB454E440018E3BB52454D000AE3BC54  .END....REM....T
 E2D0 02D0   524F4E0006E3BB54524F46460007E3BB  RON....TROFF....
 E2E0 02E0   5748494C450019EA41504F4B450008E7  WHILE...APOKE...
 E2F0 02F0   B344494D0009EA0E454550001BE7CC50  .DIM....EEP....P
 E300 0300   4F525441001CE3D8504F525442001DE3  ORTA....PORTB...
 E310 0310   D8504F525443001EE3D8504F52544400  .PORTC....PORTD.
 E320 0320   1FE3D8494E425954450023EA3954494D  ...INBYTE.#.9TIM
 E330 0330   450024EA49524554490027E3BB504143  E.$.IRETI.'..PAC

The BUFFALO monitor is a freeware M68HC11 monitor. BUFFALO is an acronym for Bit User Fast Friendly Aid to Logical Operation.

The partial asciification of the Buffalo monitor, is shown in Listing 2. It looks like a command table starting about $E543, followed by the Buffalo sign-on string at $E61F, followed by error messages.

Listing 2.

 E540 0540   BE3905415353454DF2F905425245414B  .9.ASSEM...BREAK
 E550 0550   E6DD0442554C4BE7980742554C4B414C  ...BULK...BULKAL
 E560 0560   4CE79D0443414C4CE8A30444554D50E7  L...CALL...DUMP.
 E570 0570   A60446494C4CE83B02474FE8FB044845  ..FILL.;.GO...HE
 E580 0580   4C50EA7604484F5354EEE4044C4F4144  LP.v.HOST...LOAD
 E590 0590   EF82064D454D4F5259F081044D4F5645  ...MEMORY...MOVE
 E5A0 05A0   F1340750524F43454544E8E908524547  .4.PROCEED...REG
 E5B0 05B0   4953544552F1C50653544F504154E9D1  ISTER...STOPAT..
 E5C0 05C0   055452414345E98C06564552494659EF  .TRACE...VERIFY.
 E5D0 05D0   7A013FEA760558424F4F54FDB1034153  z.?.v.XBOOT...AS
 E5E0 05E0   4DF2F9024246E83B04434F5059F13405  M...BF.;.COPY.4.
 E5F0 05F0   4552415345E798024D44E7A6024D4DF0  ERASE...MD...MM.
 E600 0600   81025244F1C502524DF1C50452454144  ..RD...RM...READ
 E610 0610   F13402544DEEE40454455354FE29FF42  .4.TM...TEST.).B
 E620 0620   554646414C4F20332E322028696E7429  UFFALO 3.2 (int)
 E630 0630   202D2042697420557365722046617374   - Bit User Fast
 E640 0640   20467269656E646C792041696420746F   Friendly Aid to
 E650 0650   204C6F676963616C204F706572617469   Logical Operati
 E660 0660   6F6E04576861743F04546F6F204C6F6E  on.What?.Too Lon
 E670 0670   670446756C6C044F702D2004726F6D2D  g.Full.Op- .rom-
 E680 0680   04436F6D6D616E643F04426164206172  .Command?.Bad ar
 E690 0690   67756D656E74044E6F20686F73742070  gument.No host p
 E6A0 06A0   6F727420617661696C61626C6504646F  ort available.do
 E6B0 06B0   6E6504636865636B73756D206572726F  ne.checksum erro
 E6C0 06C0   72046572726F72206164647220047265  r.error addr .re
 E6D0 06D0   636569766572206572726F7204BDE2FA  ceiver error....

The disassembler

Disassembly is the conversion of machine instructions into their assembly language equivalents. An output line produced by the MacLarry disassembler consists of an unmodified S-record address, then the sum of the number of bytes fetched and the EPROM base address (which defaults to zero). One (or two if the byte is $18, $1A or $CD) byte(s) of opcode precedes one or more bytes of operand if present (the inherent addressing mode has no operand). The mnemonic then is followed by the hex operand if present.. A typical disassembled line looks something like Figure 2.


Figure 2. MacLarry sample output.

Listings 3 and 4 provide examples.

Listing 3 shows the disassembly of the beginning of version 1.13 of BASIC11.S19. The added comments indicate the Reset entry point accessed via the address stored at $FFFE-$FFFF.

Listing 3.

Disass -- bas113.s19
EPROM Addr  $0000
Offset -- 0 bytes
Start     19:39:09
Finish    19:39:20

E000 0000  7E ED 30     jmp   $ED30
E003 0003  BD EC D6     jsr   $ECD6
E006 0006  B6 60 08     ldaa  $6008
E009 0009  81 55        cmpa  #$55
E00B 000B  26 03        bne   $03
E00D 000D  BD F1 16     jsr   $F116
E010 0010  BD E0 60     jsr   $E060
E013 0013  8E DF FF     lds   #$DFFF
E016 0016  0E           cli
E017 0017  4F           clra
E018 0018  5F           clrb
E019 0019  DD 3A        std   $3A
E01B 001B  DD 3F        std   $3F
E01D 001D  DD 41        std   $41
E01F 001F  BD E0 A3     jsr   $E0A3
E022 0022  7F 00 19     clr   $0019
E025 0025  7F 00 18     clr   $0018
E028 0028  7F 00 1D     clr   $001D
E02B 002B  BD E0 F4     jsr   $E0F4
	.
ED27 0D27  97 1C        staa  $1C
ED29 0D29  B7 A0 00     staa  $A000
ED2C 0D2C  7F 00 37     clr   $0037
ED2F 0D2F  39           rts
ED30 0D30  86 93        ldaa  #$93	; reset vector
						                    ; points here
ED32 0D32  B7 10 39     staa  $1039
ED35 0D35  86 03        ldaa  #$03
ED37 0D37  B7 10 24     staa  $1024
ED3A 0D3A  8E DF FF     lds   #$DFFF
ED3D 0D3D  CE 00 C4     ldx   #$00C4
ED40 0D40  18 CE ED C2  ldy   #$ED
ED44 0D44  C6 14        ldb   #$14
ED46 0D46  86 7E        ldaa  #$7E
ED48 0D48  A7 00        staa  $00,x
ED4A 0D4A  08           inx
ED4B 0D4B  1A EF 00     sty   $00,x
ED4E 0D4E  08           inx
ED4F 0D4F  08           inx
ED50 0D50  5A           decb
ED51 0D51  26 F5        bne   $F5
ED53 0D53  97 9E        staa  $9E
ED55 0D55  97 A1        staa  $A1
ED57 0D57  CC EC 79     ldd   #$EC79
ED5A 0D5A  DD 9F        std   $9F
ED5C 0D5C  CC EC 6E     ldd   #$EC6E
ED5F 0D5F  DD A2        std   $A2
ED61 0D61  CE 00 F7     ldx   #$00F7
ED64 0D64  CC ED 30     ldd   #$ED30
ED67 0D67  ED 01        std   $01,x
ED69 0D69  ED 04        std   $04,x
ED6B 0D6B  ED 07        std   $07,x
ED6D 0D6D  CE 00 A4     ldx   #$00A4
ED70 0D70  C6 20        ldb   #$20
ED72 0D72  6F 00        clr   $00,x
ED74 0D74  08           inx
ED75 0D75  5A           decb

In Listing 4, you can see the disassembly of the beginning of version 3.2 of the Buffalo monitor. The comments highlight the $E000 entry point.

Listing 4.

Disass -- buf32.s19
EPROM Addr  $0000
Offset -- 0 bytes
Start     19:40:05
Finish    19:40:16

E000 0000  CE 10 0A     ldx   #$100A ;reset vector $FFFE-$FFFF
E003 0003  1F 00 01 03  brclr $00,x  $01 $03 ; points to $E000
E007 0007  7E B6 00     jmp   $B600  ;test pin 1 at addr $100A
E00A 000A  86 93        ldaa  #$93 ;[port E]; branch to $E00A
E00C 000C  B7 10 39     staa  $1039 ; if pin 1 low, else jmp to
E00F 000F  86 00        ldaa  #$00 ;512 byte reset routine at
E011 0011  B7 10 24     staa  $1024 ;$B600 in internal EEPROM
E014 0014  86 00        ldaa  #$00
E016 0016  B7 10 35     staa  $1035
E019 0019  8E 00 65     lds   #$0065
E01C 001C  BD E3 4B     jsr   $E34B
E01F 001F  CE 00 47     ldx   #$0047

Offset and address options

The two user options - offset and EPROM base address - affect disassembly. Since most real-world code contains embedded data, tables, and other things that aren't opcodes and operands, it is useful to be able to vary the point at which disassembly begins, "throwing away" a user-specified number of data bytes from the beginning of the S-record file. It is frequently necessary to combine pieces from several disassemblies with different offsets to accurately reconstruct the skeleton of the source code.

In addition, S-record addresses may or may not correspond to the actual EPROM addresses where the code runs. For example, in the case of the BASIC11E9 distributed by New Micros, S-record addresses occupy the range $0000-$1FFF, yet the code is programmed into an EPROM that is addressed at $E000-$FFFF (2764 8k-by-8 size). Address references are easiest to assess when the corrected addresses are provided by the disassembler. The EPROM address also affects the asciification listing, showing the location of text in processor memory. MacLarry's menu allows the user to vary the offset in the range 0-9 bytes and the EPROM base address in the range $0000-$E000 in $1000 steps.

In listings 1-4 in this article, the S-record addresses run from $E000 through $FFFF and the EPROM address option is left at the zero default.

Program Structure

MacLarry makes three separate passes through the S-record file, creating first the "asc" file, followed by the "dis", and concluding with the "src" file. In all cases output is initially written to a temporary file, then copied to the final output file after dissection is complete. In pseudocode, minus the folderol and simplified, the main program loop looks like this:

for n:= 1 to 3 do
  begin
    open s-rec file
    open tmp file
    case n of
      1:
        asciify
      2:
        disassemble
      3:
        make sourcecode
    end;
    close s-rec file
    copy tmp file to final file
  end;

The asciifier simply reads each line from the input file, strips out the header and byte count, then formats an output line consisting of address, corrected address, hex data, and the printable ASCII characters in the data; characters outside the printable ASCII range #32-#126 are each represented by a dot (.).

The disassembler structure is more complex but logical. After opening the input and output files, the program fetches and counts but otherwise ignores the number of bytes specified in the offset parameter. In pseudocode, you might do it like this:

count = 0 
while (not eof) and (count < offset)
 	fetch data byte 
	increment count 
wend

The main loop follows:

while (not eof) and (not error)
  	fetch data byte
  	if not making assembler sourcecode 
    		send S-record address to outfile 
    		send EPROM address to outfile
  	endif 
	process data byte as opcode 
wend

The Main File

Listings 5a and 5b show the contents of the main program file, MacLarry.p . After calling the Initialize procedure to init the toolbox, window, global variables and such, the program calls the DoMain procedure in Listing 5b, which serves as the program's manager. When it returns from DoMain it calls the procedure CleanUpMess, whose function should be obvious, before terminating.


Listing 5.a

MacLarry

(*
    MacLarry 
    A Motorola M68HC11 S-record Dissecter
    F.C. Kuechmann
    1998-9
*)

Program MacLarry;
uses  
  Globals,Inits,Misc,FileStuff,EventStuff,TimeStuff,
  Disassembler,Sourcer,DoAski;
 begin    
  Initialize;
  InitRegionHdl;
  DoMain;
  KillRegionHdl;    
  CleanUpMess;
end.

DoMain first initializes some local variables before entering two nested repeat loops. The inner loop executes, continually checking events and updating the time, offset and EPROM address displays, until the goFlag is set by pushing the "Go" button or the doneFlag is set by pushing the "Quit" button. If the "Quit" button was pushed, the Leave instruction exits the outer loop and the procedure is exited thereafter. If the "Go" button was pushed a file selection window appears displaying the 'TEXT' files in the current directory. If no selection is made, the outer loop is exited. If a selection is made, a for loop that serves as dispatcher executes three times, opening files and calling the asciifier, disassembler and sourcer code in succession. The proper output file is created during each iteration and the header, start and finish times written to the output file before the temporary file contents are added. In the case of the "dis" and "src" files, the EPROM address and offset values are also written to the output file. After completion of the for loop a repeat loop executes until either the "Go" button is pushed to dissect another file (or perhaps the same file with a different offset value) or the "Quit" button is pushed to exit.


Listing 5b.

DoMain

  procedure DoMain;
  var
    selectFlag,eofFlag,errFlag,doneFlag,
                          goFlag,flag:boolean;
    fileName,textLine,outLine,outLine1,
                          outLine2,S,S1,S2:Str255;
    nowTime,startTime,startTime2,endTime:DateTimeRec;
    index,offset,n:integer;
    epromAddr:word;
    nowSecs,startSecs:longint;
  const
    cDoneStr:string='Done!';

  begin
    index:=1;
    n:=0;
    startSecs:=0;
    ClearWindow;
    ShowWindow(ggMonkWindow);
    errFlag:=FALSE;
    doneFlag:=FALSE;
    goFlag:=FALSE;
    flag:=TRUE;
    outLine:=ggcNullString;
    outLine1:=ggcNullString;
    outLine2:=ggcNullString;

    repeat
      epromAddr:=ggwEpromAddr;
      offset:=ggThrowAway;
      SetControlTitle(ggGoButtonHdl,'Run');
      repeat
        GetDateTime(nowSecs);
        if nowSecs<>startSecs then
          begin
            GetTime(startTime);
            Time2String(startTime,S2);
            UpdateRect(ggcBeginTime,S2);
            startSecs:=nowSecs;
          end;
        HandleEvent(doneFlag,goFlag);
        if offset<>ggThrowAway then
          begin
            offset:=ggThrowAway;
            UpdateRect(ggcOffsetRect,ggcNullString);
          end;
        if epromAddr<>ggwEpromAddr then
          begin
            epromAddr:=ggwEpromAddr;
            UpdateRect(ggcEpromRect,ggcNullString);
          end;
      until goFlag or doneFlag;
      if doneFlag then
        leave;
      SetControlTitle(ggGoButtonHdl,'Pause');
      SelectFile(selectFlag,fileName);
      ClearWindow;

      if not selectFlag then
        leave;

      GetTime(startTime);
      Time2String(startTime,S2);
      UpdateRect(ggcBeginTime,S2);
      DateToSeconds(startTime,startSecs);

      outLine1:=Word2HexStr(ggwEpromAddr);
      outLine1:=concat('EPROM Addr  $',outLine1);

      NumToString(ggThrowAway,outLine2);
      outLine2:=concat('Offset -- ',outLine2,' bytes');

      for index:=1 to 3 do

        begin
          InitDisDatLin;
          errFlag:=FALSE;
          OpenSrecFile(fileName,errFlag);
          if errFlag then
            leave;

            {output goes first to a temporary file}
            {that is copied to the final out file}
            {with start and end times both at the}
            {beginning}
          OpenTmpFileWrite(errFlag);
          if errFlag then
            leave;

          GetTime(startTime2);
          Time2String(startTime2,S1);
          case index of
            ggcMakeAski:
              begin
                S:='Ascifying '+fileName;
                AscifyFile(startSecs,doneFlag);
                UpdateRect(ggcAsky,cDoneStr);
              end;
            ggcMakeDis:

              begin
                S:='Disass -- '+fileName;
                DisDatFile(startSecs,doneFlag);
              end;
            ggcMakeDat:

              begin
                S:='Sourc --- '+fileName;
                SourceDatFile(startSecs,doneFlag);
              end;
          end;
          GetTime(endTime);
          Time2String(endTime,S2);
          CloseSrecFile(errFlag);

          CloseTmpFile(errFlag);
          if errFlag then
            leave;

            {open temp file for input}
          OpenTmpFileRead(errFlag);
            {create & open destination file}
          OpenOutFile(fileName,index,errFlag,TRUE);
          if errFlag then
            leave;
            {asc, dis or src line}
          WriteToOutFile(S,errFlag);
            {EPROM addr & offset to disass & src files}
          if index>ggcMakeAski then

            begin
              WriteToOutFile(outLine1,errFlag);
              WriteToOutFile(outLine2,errFlag);
            end;
          if not errFlag then

            begin
                {beg & fin times}
              WriteToOutFile('Start     '+S1,errFlag);
              WriteToOutFile('Finish    '+S2,errFlag);
                {send a few blank lines}
              for n:=1 to 3 do
                WriteToOutFile(ggcNullString,errFlag);
              eofFlag:=FALSE;
                {read tmp, write final}
              while not (eofFlag or errFlag) do

                begin
                  ReadFromTmpFile(S,eofFlag,errFlag);
                  WriteToOutFile(S,errFlag);
                end;
                {shoot a few blanks}
              for n:=1 to 3 do
                WriteToOutFile(ggcNullString,errFlag);

              CloseTmpFile(errFlag);
                {erase tmp file for re-use}
              ScratchTmpFile(errFlag);
              CloseOutFile(index,errFlag,flag);
              EraseErrMess;
            end;
        end;  {for}

      UpdateS19Rect(ggcNullString,2);
      goFlag:=FALSE;
      SetControlTitle(ggGoButtonHdl,'Run');
      repeat
        HandleEvent(doneFlag,goFlag);
      until goFlag or doneFlag or errFlag;
      EraseErrMess;
    until doneFlag or errFlag;

Creating assembler sourcecode

The asciifier, disassembler and sourcer routines all fetch lines one-at-a-time from the input file and dissect them in the appropriate manner, blessing the output files with the results.

The following disassembly code listings are for creation of assembler source files ("src" filename extension) with a space in the label field left of the mnemonic. The code for "dis" files is similar, but the output lines contain address and data fields left of the mnemonic.

For disassemblies a pair of hex digits is chopped from the data segment of the s-record and converted to an unsigned byte integer value. It is then passed to a ProcessOpCode procedure shown in Listing 6. There, the opcode is tested against the values held in sets of byte constants whose membership is arranged according to addressing mode.

  • cPostByteSet membership indicates we've got the first byte of a two-byte opcode and need to fetch another byte to find out what to do. Postbyte instructions operate on the Y-index register and D-accumulator.
  • cRelSet instructions are relative branches in the range -128..+127 addresses from the current address and require fetching a single operand byte holding the offset.
  • cInhSet opcodes consist of complete instructions requiring no operands.
  • cIndxSet opcodes use the X-index register and need a variable number of operand bytes.
  • cImmSet opcodes have data operands rather than addresses.
  • cExtSet opcodes use extended addressing with two byte operands.
  • cDirSet opcodes use direct addressing with single byte operands.

Listing 6.

ProcessOpCode

  procedure ProcessOpCode(opcode:byte;
                        var errFlag,eofFlag:boolean);

  const
    cPostByteSet :ggtByteSet=[$18,$1a,$cd];
    cRelSet      :ggtByteSet=[$20..$2f,$8d];
    cInhSet      :ggtByteSet=[$00..$0f,$10,$11,$16,
                              $17,$19,$1b,$30..$3f,
                              $40,$43,$44,$46..$4a,
                              $4c,$4d,$4f,$50,$53,$54,
                              $56..$5a,$5c,$5d,$5f,
                              $8f,$cf];
    cIndxSet     :ggtByteSet=[$1c..$1f,$60,$63,$64,
                              $66..$6a,$6c..$6f,
                              $a0..$af,$e0..$ef];
    cImmSet      :ggtByteSet=[$80..$86,$88..$8c,
                              $8e,$c0..$c6,
                              $c8..$cc,$ce];
    cExtSet      :ggtByteSet=[$15,$70,$73,$74,
                              $76..$7a,$7c..$7f,
                              $ad,$b0..$bf,$f0..$ff];
    cDirSet      :ggtByteSet=[$12..$14,$90..$9f,$d0..$df];

  begin
    if opcode in cPostByteSet then
      ProcessPostByte(opcode,errFlag,eofFlag)

    else if opcode in cInhSet then
      ProcessInherent(opcode,errFlag,eofFlag)

    else if opcode in cIndxSet then
      ProcessIndexX(opcode,errFlag,eofFlag)

    else if opcode in cRelSet then
      ProcessRelative(opcode,errFlag,eofFlag)

    else if opcode in cImmSet then
      ProcessImmediate(opcode,errFlag,eofFlag)

    else if opcode in cExtSet then
      ProcessExt(opcode,errFlag,eofFlag)

    else if opcode in cDirSet then
      ProcessDirect(opcode,errFlag,eofFlag)
    else

      ProcessNotInSets(opcode,errFlag);
  end;

Processing inherently

Converting inherent addressing mode instructions is simplest in that the operand is inherent in the opcode and need not be fetched separately. The ProcessInherent procedure is shown in Listing 7. Using the opcode as the selector in a case statement, the mnemonic is assigned to the string variable s. Variable s is then concatenated with the string variable srcStr, which holds a space, and sent to the output file.


Listing 7.

ProcessInherent

  procedure ProcessInherent(opcode:byte;
                        var errFlag,eofFlag:boolean);
  var
    s,srcStr:str255;
  begin
    srcStr:=ggcSpace;
    case opcode of
      $00 :
        s:='test';
      $01 :
        s:='nop';
      $02 :
        s:='idiv';
      $03 :
        s:='fdiv';
          .
          .
          . 

      $58 :
        s:='aslb';
      $59 :
        s:='rolb';
      $5a :
        s:='decb';
      $5c :
        s:='incb';
      $5d :
        s:='tstb';
      $5f :
        s:='clrb';
      $8f :
        s:='xgdx';
      $cf :
        s:='stop';
    end;                         {case}

    srcStr:=concat(srcStr,s);
    PutString(srcStr,errFlag);
  end;

Other address modes

Addressing modes other than inherent require one, two or three byte operands to be fetched from the input text and attached to the output string. The direct address mode offers a simple illustration of how this can be done.

Direct mode

The procedure ProcessDirect, in Listing 8, defines two sets of constants that hold the values of the opcodes that require more than one operand byte.


Listing 8.

ProcessDirect

  procedure ProcessDirect(opcode:byte;
                      var errFlag,eofFlag:boolean);
  var
    s,operandStr,srcStr:str255;
    operand:byte;
    badHexFlag:boolean;
  const
    cTwoByteOperSet    :ggtByteSet=[$14];
    cThreeByteOperSet  :ggtByteSet=[$12,$13];

  begin
    badHexFlag:=FALSE;
    srcStr:=ggcSpace;
    case opcode of                     {inst xx}
      $12:                      {brset dd mm xx}
        s:='brset';
      $13:                      {brclr dd mm xx}
        s:='brclr';
      $14:                      {bset dd mm}
        s:='bset';
           .
           .
           .
      $de :
        s:='ldx';
      $df :
        s:='stx';
    end;                         {case}

    srcStr:=concat(srcStr,s);
    FetchByte(s,errFlag,eofFlag);
    if not HexStr2Byte(s,operand) then
      badHexFlag:=TRUE
    else

      begin
        operandStr:=concat('$',s);
        if (opcode in cTwoByteOperSet) or 
                      (opcode in cThreeByteOperSet) then

          begin
            FetchByte(s,errFlag,eofFlag);
            if not HexStr2Byte(s,operand) then
              badHexFlag:=TRUE
            else

              begin
                operandStr:=concat(operandStr,
                                  ggcSpace,'$',s);
                if opcode in cThreeByteOperSet then
                  begin
                    FetchByte(s,errFlag,eofFlag);
                    if not HexStr2Byte(s,operand) then
                      badHexFlag:=TRUE
                    else 
										 operandStr:=concat(operandStr,
                                    ggcSpace,'$',s);
                  end;
              end;
          end;
      end;

    if not badHexFlag then
      srcStr:=concat(srcStr,ggcSpace,operandStr)
    else
			srcStr:=concat(srcStr,ggcSpace,operandStr,
        				ggcSpace,s,ggcSpace,' - invalid hex byte!');
    PutString(srcStr,errFlag);
  end;

The opcode is used as the selector in a case statement to assign the proper mnemonic to a string variable; most of the case statement is omitted from the listing to enhance clarity. Next the first operand byte is fetched from the input string and joined to the output string. Additional bytes are fetched for the members of the two and three byte sets and appended. The completed line is then sent to output.

Indexed instructions

Index register instructions are somewhat more complicated in formatting in that they often require inclusion of an "x" or "y" in the output line. Instructions for the "X" register are handled by the code in Listing 9.


Listing 9.

ProcessIndexX

  procedure ProcessIndexX(opcode:byte;
                      var errFlag,eofFlag:boolean);
  var
    s,operandStr,srcStr:str255;
    badHexFlag:boolean;
    operand:byte;
  const
    cTwoByteSet       :ggtByteSet=[$1c,$1d];
    cThreeByteSet     :ggtByteSet=[$1e,$1f];

  begin
    badHexFlag:=FALSE;
    srcStr:=ggcSpace;
    case opcode of

      $1c:                      {bset (ind,x) dd}
        s:='bset';
      $1d:                      {bclr (ind,x) dd}
        s:='bclr';
      $1e:                      {brset (ind,x) xx dd}
        s:='brset';
      $1f:                      {brclr dd,x xx dd}
        s:='brclr';
      $60 :
        s:='neg';
      $63 :
        s:='com';
      $64 :
        s:='lsr';
      $67:                      {asr (ind,x)}
        s:='asr';
      $68:                      {asl (ind,x)}
        s:='asl';
          .
      $ec :
        s:='ldd';
      $ed :
        s:='std';
      $ee :
        s:='ldx';
      $ef :
        s:='stx';
    end;

    srcStr:=concat(srcStr,s);
    FetchByte(s,errFlag,eofFlag);
    if not HexStr2Byte(s,operand) then
      badHexFlag:=TRUE
    else

      begin
        operandStr:=concat('$',s,',x');
        if (opcode in cTwoByteSet) or 
                    (opcode in cThreeByteSet) then

          begin
            FetchByte(s,errFlag,eofFlag);
            if not HexStr2Byte(s,operand) then
              badHexFlag:=TRUE
            else

              begin
                operandStr:=concat(operandStr,
                                    ggc2Spaces,'$',s);
                if opcode in cThreeByteSet then

                  begin
                    FetchByte(s,errFlag,eofFlag);
                    if not HexStr2Byte(s,operand) then
                      badHexFlag:=TRUE
                    else
                      operandStr:=concat(operandStr,
                                        ggcSpace,'$',s);
                  end;
              end;
          end;
      end;

    if not badHexFlag then
      srcStr:=concat(srcStr,ggcSpace,operandStr)
    else

      srcStr:=concat(srcStr,ggcSpace,operandStr,
						ggcSpace,s,ggcSpace,' - invalid hex byte!');
    PutString(srcStr,errFlag);
  end;

The opcode bytes twice

The "post byte" instructions offer a somewhat different form of complexity in that the first thing to be done is fetch the "real" opcode. The two-byte opcodes are made neccessary by the fact that a one-byte opcode can hold only 256 possible values. When Motorola started designing 8-bit processors with more than one index register, they needed more than 256 opcodes. Their solution with processors like the 6809 and 68HC11 is a number of two byte opcodes. In the case of the 68HC11 opcodes whose first bytes are $18, $1A or $CD use a second byte for the actual instruction. With the 68HC11 most post byte instructions use the Y-index register and many directly mirror X-register instructions with the same opcodes, although some affect the D-accumulator.

Initial processing of $1A instructions is shown in Listing 10.


Listing 10

Process1A

  procedure Process1A(var errFlag,eofFlag:boolean);
  var
    s,operandStr,srcStr:str255;
    opcode,operand:byte;
    badHexFlag:boolean;

  begin
    badHexFlag:=FALSE;
    srcStr:=ggcSpace;

    FetchByte(s,errFlag,eofFlag);
    if not HexStr2Byte(s,opcode) then

      begin
        SendBadHexMess(errFlag);
        exit;
      end;

    case opcode of
      $83:                      {cpd #dddd}
        s:='cpd';
      $93:                      {cpd dd}
        s:='cpd';
      $a3:                      {cpd dd,x}
        s:='cpd';
      $ac:                      {cpy dd,x}
        s:='cpy';
      $b3:                      {cpd dddd}
        s:='cpd';
      $ee:                      {ldy dd,x}
        s:='ldy';
      $ef:                      {sty dd,x}
        s:='sty';
      else

        begin
          ProcessNotInSets(opcode,errFlag);
          exit;
        end;
    end;                         {case}

    srcStr:=concat(srcStr,s);
    FetchByte(s,errFlag,eofFlag);
    if not HexStr2Byte(s,operand) then
      badHexFlag:=TRUE
    else

      begin
        case opcode of
          $83 :

            begin
              operandStr:=concat('#$',s);
              FetchByte(s,errFlag,eofFlag);
              if not HexStr2Byte(s,operand) then
                badHexFlag:=TRUE
              else
                operandStr:=concat(operandStr,s);
            end;
          $b3,$93 :

            begin
              operandStr:=concat('$',s);
              if opcode=$b3 then

                begin
                  FetchByte(s,errFlag,eofFlag);
                  if not HexStr2Byte(s,operand) then
                    badHexFlag:=TRUE
                  else
                    operandStr:=concat(operandStr,s);
                end;
            end;

          otherwise
            operandStr:=concat('$',s,',x');
        end;
      end;

    if not badHexFlag then
      srcStr:=concat(srcStr,ggcSpace,operandStr)
    else
      srcStr:=concat(srcStr,ggcSpace,s,
                    ggcSpace,' - invalid hex byte!');
    PutString(srcStr,errFlag);

  end;

The second opcode byte is fetched and converted to numeric form, then used in a case statement to select the instruction mnemonic. Operand bytes are fetched as needed to compose the output string.

The modes not shown

The rest of the addressing modes are handled in a manner similar to these examples with the exception of the relative address mode whose code is shown in Listing 11.

Relatively coding

Except for the "bsr" (branch to subroutine) instruction, whose code is $8d, the opcodes occupy the range $20..$2F. The mnemonics for those sixteen codes are contained in an array of three-character strings indexed by the opcodes. Using a set [$20..$2F] for a qualifier, an array element or the string 'bsr' is copied to string variable s. The single operand byte is then fetched and the output line assembled.


Listing 11.

ProcessRelative

  procedure ProcessRelative(opcode:byte;
                        var errFlag,eofFlag:boolean);
  type
    tOpStrRay = array [$20..$2f] of ggtString3;
  var
    s,operandStr,srcStr:str255;
    badHexFlag:boolean;
    index:integer;
    operand:byte;

  const
    cRelSet      :ggtByteSet=[$20..$2f];
    cRelCodes    :tOpStrRay=('bra','brn','bhi','bls',
                             'bcc','bcs','bne','beq',
                             'bvc','bvs','bpl','bmi',
                             'bge','blt','bgt','ble');

    c8dCode   :ggtString3='bsr';

  begin
    badHexFlag:=FALSE;
    srcStr:=ggcSpace;
    if opcode in cRelSet then
      s:=cRelCodes[opcode]
    else if opcode=$8d then
      s:=c8dCode;
    srcStr:=concat(srcStr,s);
    FetchByte(s,errFlag,eofFlag);
    if not HexStr2Byte(s,operand) then
      badHexFlag:=TRUE

    else
      s:=concat('$',s);
    if not badHexFlag then
      srcStr:=concat(srcStr,ggcSpace,s)
    else
      srcStr:=concat(srcStr,ggcSpace,s,
                  ggcSpace,' - invalid hex byte!');
    PutString(srcStr,errFlag);

  end;

Jack and Jill went up the hill to fetch a pail of bytes

You may have noticed repeated calls to the FetchByte procedure in the previous code listings. FetchByte, shown in Listing 12, is MacLarry's operating engineer. It retrieves S-records from the input file and chops them up, delivering bytes of data as needed. Available data bytes are held in global string variable gS19Line, which is initialized to an empty string. If its length is zero when we enter the while loop, a new S-record is fetched from the input file. The header is stripped off and if it is "S9" we've reached the end of the file; if the header is neither "S1" nor "S9", the badLineFlag is TRUE - in either case we exit without further ado. If the header was "S1" we strip off the byte count, address and checksum and assign the remaining text, presumed to be bytes of useful data, to the global variable gS19Line. Finally, we chop off a byte of hex data to take home.

If the length of the string gS19Line is greater than zero when we enter the while loop, we simply chop off a byte of data and leave the loop on the next conditional check.


Listing 12.

FetchByte

  procedure FetchByte(var s:str255;
                   var errFlag,eofFlag:boolean);
  var
    s2,txt        :str255;
    c             :char;
    I,numOfBytes :byte;
    S9Flag,badLineFlag:boolean;

  begin
    s:=ggcNullString;
    eofFlag:=FALSE;
    errFlag:=FALSE;

    while (not (eofFlag or errFlag)) and 
                                (s=ggcNullString) do

      begin
        s:=ggcNullString;
        if length(gS19Line)<1 then

          begin
            FetchLine(txt,eofFlag,errFlag);
            if not errFlag then

              begin
                S9Flag:=FALSE;
                StripHeader(txt,badLineFlag,S9Flag);
                if S9Flag or badLineFlag then

                  begin
                    txt:='';
                    exit;
                  end
                else

                  begin
                    StripByteCount(txt,numOfBytes);
                    StripAddr(txt);
                    StripChecksum(txt);
                    gS19Line:=txt;
                  end;
              end;
          end;

         				 {chop off byte to return with}
        s:=copy(gS19Line,1,2);
        Delete(gS19Line,1,2);
        Inc(gByteTotal);
      end;                       {while}

  end;

Running MacLarry

When you run MacLarry the window appears and nothing much happens other than the relentless advance of the clock at upper right. The offset and EPROM address values, which both default to zero, are displayed below the time. Use the Address, Offset, and Speed menus to make any needed changes from defaults, then punch the "Go" button. Select the S-record file to be dissected and click "Open". (A sample S-record file, buf32.s19, is provided in the archive.) That's it. You'll see the ASCII scroll by in the top window, followed by successive disassemblies in the big window below.

To continue with dissections, adjust the offset, address and speed values, then punch "Go" to repeat.

Conclusion

While MacLarry might not be an expensive microprocessor development tool, it is a useful utility for poking around inside M68HC11 S-record files. In conjunction with an assembler it can be a useful learning aid for embedded controller assembly language.

The files available for download include CodeWarrior Pro Pascal projects and sourcecode, as well as compiled 68k and PPC apps. Freeware 68HC11 Mac cross-assemblers, S-recordfiles for the BASIC11 interpreter and BUFFALO monitor for dissecting are available at the URLs noted in the references.

Happy dissecting.

References

  • Motorola, M68HC11 Reference Manual; lit. ref. #M68HC11RM/AD; the "bible" of the M68HC11 family and a must-have for any serious M68HC11 programmer. In addition, there are many supplementary manuals to document the numerous versions of the microcontroller. Most of the manuals can be downloaded in PDF form from the Motorola M68HC11 documentation page. A CD containing several manuals is also available directly from Motorola at no cost.
  • Skroder, John, Using the M68HC11 Microcontroller: A Guide to Interfacing and Programming,. Prentice Hall, 1996; ISBN 0-13-120676-1. Integrated lab/text format.
  • F. Driscoll, R. Coughlin and R. Villanucci; Data Acquisition and Process Control with the M68HC11 Microcontroller , Macmillan Publishing Company, 1994, ISBN 0-02-330555-X
  • G. H. Miller, Microcomputer Engineering; Prentice Hall, 1993, ISBN 0-13-584475-4. The basics. Assembly language programs. Written for use with the Motorola M68HC11-EVB trainer board.
  • P. Spasov; Microcontroller Technology, The 68hc11 ; Prentice Hall, 1996, ISBN 0-13-362724-1; Hardware and software. A good book for M68HC11 novices.
  • R. J. Tocci, L. P. Laskowski and F. J. Ambrosio, Microprocessors and Microcomputers: Hardware and Software; Prentice Hall, 1996; ISBN 0-13-235946-4
  • J. D. Greenfield, The 68HC11 Microcontroller; Saunders College Publishing, 1992; ISBN 0-03-051588-2

Some of the best documentation for the M68HC11, as well as many versions of both BASIC11 and the BUFFALO monitor, can be downloaded at no charge from Motorola's web site. The following URLs will get you started on this documentation.

One of the best sources of information on embedded systems in general is a monthly magazine called Circuit Cellar <http://www.circuitcellar.com/>, founded by Steve Ciarcia, the popular former Byte contributor and one of the mainstays of that publication during its 1980s prime.


F.C. Kuechmann is a programmer, hardware designer and consultant with degrees from the University of Illinois at Chicago and Clark College. His favorite programming language is Pascal and his favorite programming utility is a large mug of kaffee mocha. He can be reached at fk@aone.com.

 
AAPL
$102.50
Apple Inc.
+0.25
MSFT
$45.43
Microsoft Corpora
+0.55
GOOG
$571.60
Google Inc.
+2.40

MacTech Search:
Community Search:

Software Updates via MacUpdate

VueScan 9.4.41 - Scanner software with a...
VueScan is a scanning program that works with most high-quality flatbed and film scanners to produce scans that have excellent color fidelity and color balance. VueScan is easy to use, and has... Read more
Cloud 3.0.0 - File sharing from your men...
Cloud is simple file sharing for the Mac. Drag a file from your Mac to the CloudApp icon in the menubar and we take care of the rest. A link to the file will automatically be copied to your clipboard... Read more
LibreOffice 4.3.1.2 - Free Open Source o...
LibreOffice is an office suite (word processor, spreadsheet, presentations, drawing tool) compatible with other major office suites. The Document Foundation is coordinating development and... Read more
SlingPlayer Plugin 3.3.20.505 - Browser...
SlingPlayer is the screen interface software that works hand-in-hand with the hardware inside the Slingbox to make your TV viewing experience just like that at home. It features an array of... Read more
Get Lyrical 3.8 - Auto-magically adds ly...
Get Lyrical auto-magically add lyrics to songs in iTunes. You can choose either a selection of tracks, or the current track. Or turn on "Active Tagging" to get lyrics for songs as you play them.... Read more
Viber 4.2.2 - Send messages and make cal...
Viber lets you send free messages and make free calls to other Viber users, on any device and network, in any country! Viber syncs your contacts, messages and call history with your mobile device,... Read more
Cocktail 7.6 - General maintenance and o...
Cocktail is a general purpose utility for OS X that lets you clean, repair and optimize your Mac. It is a powerful digital toolset that helps hundreds of thousands of Mac users around the world get... Read more
LaunchBar 6.1 - Powerful file/URL/email...
LaunchBar is an award-winning productivity utility that offers an amazingly intuitive and efficient way to search and access any kind of information stored on your computer or on the Web. It provides... Read more
Maya 2015 - Professional 3D modeling and...
Maya is an award-winning software and powerful, integrated 3D modeling, animation, visual effects, and rendering solution. Because Maya is based on an open architecture, all your work can be scripted... Read more
BBEdit 10.5.12 - Powerful text and HTML...
BBEdit is the leading professional HTML and text editor for the Mac. Specifically crafted in response to the needs of Web authors and software developers, this award-winning product provides a... Read more

Latest Forum Discussions

See All

Qube Kingdom – Tips, Tricks, Strategies,...
Qube Kingdom is a tower defense game from DeNA. You rally your troops – magicians, archers, knights, barbarians, and others – and fight against an evil menace looking to dominate your kingdom of tiny squares. Planning a war isn’t easy, so here are a... | Read more »
Qube Kingdom Review
Qube Kingdom Review By Nadia Oxford on August 29th, 2014 Our Rating: :: KIND OF A SQUARE KINGDOMUniversal App - Designed for iPhone and iPad Qube Kingdom has cute visuals, but it’s a pretty basic tower defense game at heart.   | Read more »
Fire in the Hole Review
Fire in the Hole Review By Rob Thomas on August 29th, 2014 Our Rating: :: WALK THE PLANKUniversal App - Designed for iPhone and iPad Seafoam’s Fire in the Hole looks like a bright, 8-bit throwback, but there’s not enough booty to... | Read more »
Alien Creeps TD is Now Available Worldwi...
Alien Creeps TD is Now Available Worldwide Posted by Ellis Spice on August 29th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Dodo Master Review
Dodo Master Review By Jordan Minor on August 29th, 2014 Our Rating: :: NEST EGGiPad Only App - Designed for the iPad Dodo Master is tough but fair, and that’s what makes it a joy to play.   | Read more »
Motorsport Manager Review
Motorsport Manager Review By Lee Hamlet on August 29th, 2014 Our Rating: :: MARVELOUS MANAGEMENTUniversal App - Designed for iPhone and iPad Despite its depth and sense of tactical freedom, Motorsport Manager is one of the most... | Read more »
Motorsport Manager – Beginner Tips, Tric...
The world of Motorsport management can be an unforgiving and merciless one, so to help with some of the stress that comes with running a successful race team, here are a few hints and tips to leave your opponents in the dust. | Read more »
CalPal Update Brings the App to 2.0, Add...
CalPal Update Brings the App to 2.0, Adds Lots of New Stuff Posted by Ellis Spice on August 29th, 2014 [ permalink ] | Read more »
Baseball Battle Review
Baseball Battle Review By Jennifer Allen on August 29th, 2014 Our Rating: :: SIMPLE HITTINGUniversal App - Designed for iPhone and iPad Simple and cute, Baseball Battle is a fairly fun baseball game for those looking for something... | Read more »
Checkmark 2.1 Update Released, and it’s...
Checkmark 2.1 Update Released, and it’s on Sale for a Limited Time Posted by Jessica Fisher on August 29th, 2014 [ permalink ] | Read more »

Price Scanner via MacPrices.net

Labor Day Weekend MacBook Pro sale; 15-inch m...
B&H Photo has the new 2014 15″ Retina MacBook Pros on sale for up to $125 off MSRP. Shipping is free, and B&H charges NY sales tax only. They’ll also include free copies of Parallels Desktop... Read more
Labor Day Weekend iPad mini sale; $50 to $100...
Best Buy has the iPad mini with Retina Display (WiFi models) on sale for $50 off MSRP on their online store for Labor Day Weekend. Choose free shipping or free local store pick up. Price is for... Read more
13-inch 1.4GHz MacBook Air on sale for $899,...
Adorama has the new 2014 13″ 1.4GHz/128GB MacBook Air on sale for $899.99 including free shipping plus NY & NJ tax only. Their price is $100 off MSRP. Read more
It’s Official: Apple Issues Invitations To Se...
Apple has issued one of its characteristically cryptic press invitations for a special event to be held at the Flint Center for the Performing Arts in hometown Cupertino on Sept. 9, 2014 at 10:00 am... Read more
Tablet Shipments To See First On-year Decline...
TrendForce analyst Caroline Chen notes that when the iPad launched in 2010, it was an instant hit and spurred a tablet PC revolution, with tablets so popular that that notebook PC sales stagnated and... Read more
SOBERLINK Releases Apple iOS Compatible Handh...
Cypress, California based SOBERLINK, Inc., creator of the first handheld Breathalyzer designed to improve recovery outcomes, continues to show prominence in the mobile alcohol monitoring space with... Read more
New 21″ 1.4GHz iMac on sale again for $999, s...
Best Buy has the new 21″ 1.4GHz iMac on sale for $999.99 on their online store. Their price is $100 off MSRP. Choose free shipping or free local store pick up. Price is for online orders only, in-... Read more
Smartphone Outlook Remains Strong for 2014, U...
According to a new mobile phone forecast from the International Data Corporation (IDC) Worldwide Quarterly Mobile Phone Tracker, more than 1.25 billion smartphones will be shipped worldwide in 2014,... Read more
Save up to $60 with Apple refurbished iPod to...
The Apple Store has Apple Certified Refurbished 5th generation iPod touches available starting at $149. Apple’s one-year warranty is included with each model, and shipping is free. Many, but not all... Read more
12-Inch MacBook Air Coming in 4Q14 or 2015 –...
Digitimes’ Aaron Lee and Joseph Tsai report that according to Taiwan-based upstream supply chain insiders, Apple plans to launch a thinner MacBook model either at year end 2014 or in 2015, and that... Read more

Jobs Board

*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
Senior Event Manager, *Apple* Retail Market...
…This senior level position is responsible for leading and imagining the Apple Retail Team's global event strategy. Delivering an overarching brand story; in-store, Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.