/* single-line assembly with space and operand/address size controls;
allow advancing to next dasm line. */!!

inherit(Dialog, #AsmDlg2, #(editText /* editText object */
staticText /* static text control (label) */
helpEntry /* help Entry point */
addrDesc  /* beginning asm address */
workAddrDesc /* working address desc. */
addrSpace
operandAddrSize
offset  /* current offset */
dasmHandle /* dasm browser caller */
asmFlag /* true after first assembly done */), 2, nil)!!

now(class(AsmDlg2))!!

/* 03/15/1994 11:85 - PUBLIC
  Open a new AsmDlg2 object for the caller.
  NOTES:
    - <addrDesc> is the address descriptor containing the initial address
      to assemble.
    - dasmHandle is the disassembly browser; it's needed for
      dasm line formatting methods.
    - This class performs all modifications to memory before returning.
    - The caller is responsible for destroying the input <addrDesc>.
*/
Def open(self, addrDesc, dasmHandle, hlpEntry | dlg)
{
  dlg := new(self);
  ^init(dlg, addrDesc, dasmHandle, hlpEntry);
}!!

now(AsmDlg2)!!

/* Highlight the source edit box. */
Def highlightSourceField(self | lines selRange)
{
  /* Select source field; default behavior is only to highlight the first
     time the dialog is painted, which is not very useful since the skip
     to next line feature has been added. */
  selRange := new(Struct, 4);
  putWord(selRange, 32767, 2);  /* 0 lw & 32767 hw means select all */
  /* EM_SETSEL (0x401) not defined in Actor; in Borland "WM_USER + 1" */
  Call SendMessage(itemHandle(self, DLG_SLASM_SOURCE_TEXT), 0x401, 0,
    longAt(selRange, 0));
  setItemFocus(self, DLG_SLASM_SOURCE_TEXT);
} 
!!

/* Advance to the next statement:
   - disassemble at the current offset, receiving 2 lines.
   - grab the offset text from the second dasm line.
   - set the offset from the offset text. */
Def advanceOffsetToNextStatement(self | lines temp)
{
  if not(copyAddress(AddressLibClass$Inst, addrDesc, workAddrDesc))
    ^nil;
  endif;
  if not(setOffset(AddressLibClass$Inst, workAddrDesc, offset))
    ^nil;
  endif;
  
  /* get 2 dasm source lines */
  if not(setAddress(DisAsmLibClass$Inst, dasmServer(self), workAddrDesc))
    ^nil;
  endif;
  lines := getDasmInstructions(DisAsmLibClass$Inst, dasmServer(self), 2);
  if not(lines) cor (size(lines) < 2)
    ^nil;
  endif;
  
  /* get offset from 2nd dasm source line; try 3rd if 2nd fails (2 line source) */
  if not(temp := getAddressFieldOffset(dasmHandle, lines[1]))
    if (size(lines) > 2)
      temp := getAddressFieldOffset(dasmHandle, lines[2]);
    endif;
    if not(temp)
      ^nil;
    endif;
  endif;
  
  offset := temp;  /* getAddress successful; update offset */
  
  initSourceFields(self);
}
!!

/* PUBLIC: given a full source line, place the source only (offset removed)
   in the edit control. */
Def initSourceOffset(self, sourceLine)
{
  setItemText(self, DLG_SLASM_SOURCE_ADDR,
    "&Source Line: " +
    getAddressFieldText(dasmHandle, sourceLine));
} 
!!

/* Place the current source offset and text into their dialog controls. */
Def initSourceFields(self | lines selRange)
{
  if not(copyAddress(AddressLibClass$Inst, addrDesc, workAddrDesc))
    ^nil;
  endif;
  if not(setOffset(AddressLibClass$Inst, workAddrDesc, offset))
    ^nil;
  endif;
  
  /* get 2 dasm source lines */
  if not(setAddress(DisAsmLibClass$Inst, dasmServer(self), workAddrDesc))
    ^nil;
  endif;
  lines := getDasmInstructions(DisAsmLibClass$Inst, dasmServer(self), 1);
  if not(lines) cor (size(lines) < 1)
    ^nil;
  endif;
    
  /* place offset in label */
  initSourceOffset(self, lines[0]);
  
  /* place source text in edit control */
  setItemText(self, DLG_SLASM_SOURCE_TEXT,
    getSourceField(dasmHandle, lines[0]));
  
  highlightSourceField(self);
} 
!!

/* Process assemble button stuff. */
Def processAssemble(self | spaceTxt source status)
{
  if not(spaceTxt := getCBText(self, DLG_SLASM_SPACE_COMBO)) cor
    not(addrSpace := translateAddrSpace(AddressLibClass$Inst, spaceTxt))
      ^nil;
  endif;

  /* @@ Uncomment if we ever support operand/address size...
  // Get the new operandAddrSize for caller
  if ((addrSize := asInt(sendDlgItemMessage(self, DLG_SLASM_SIZE_COMBO, 
    CB_GETCURSEL, 0, 0))) cand (addrSize ~= LBN_ERR))   
      operandAddrSize := addrSize;
  endif;
  ... @@ */
      
  /* process source */
  source := getItemText(self, DLG_SLASM_SOURCE_TEXT);
  
  /* copy address (assembly disrupts address) */
  if not(copyAddress(AddressLibClass$Inst, addrDesc, workAddrDesc))
    ^nil;
  endif;
  if not(setOffset(AddressLibClass$Inst, workAddrDesc, offset))
    ^nil;
  endif;
  
  if (status := assembleSource(AsmLibClass$Inst, asmServer(self),
    workAddrDesc, source))
      advanceOffsetToNextStatement(self);  
  endif;
  
  /* change button from Cancel to Done after first assemble */
  if not(asmFlag)
    setItemText(self, DLG_SLASM_CANCEL_AND_DONE, asciiz("D&one"));
    asmFlag := 0;
  endif;
  
  /* refresh the dasm source and offset with the next line if
     assembly successful; leave alone if the assembly failed. */
  if status
    initSourceFields(self);
  else
    highlightSourceField(self);
  endif;
}
!!

/* return the handle to the dasm server. */
Def asmServer(self)
{
  ^asmServer(dasmHandle);
}!!

/* return the handle to the dasm server. */
Def dasmServer(self)
{
  ^dasmServer(dasmHandle);
}!!

/* Remove the "IO" selection if necessary. */
Def processAddressSpaces(self, spaceColl | temp) 
{
  ^extract(spaceColl, {using(space)  asLowerCase(space) <> "io"; });
}
!!

/* init instance variables. */
Def init(self, newAddr, dasmHandleIn, hEntry)
{ 
  if not(newAddr)
    ^nil;
  endif;
  addrDesc := newAddr;
  
  if not(dasmHandleIn)
    ^nil;
  endif;
  dasmHandle := dasmHandleIn;
  
  helpEntry := hEntry;  /* help entry point */
  
  if not(addrSpace := getAddressSpace(AddressLibClass$Inst, addrDesc))
    addrSpace := ADDR_SPACE_DEFAULT; 
  endif;
  operandAddrSize := ADDR_USE_AUTO;
  
  /* create descriptor for work address */
  if not(workAddrDesc := createAddress(AddressLibClass$Inst))
    ^nil;
  endif;
  
  if not(offset := getOffset(AddressLibClass$Inst, addrDesc))
    ^nil;
  endif;
}
!!

/* 3/15/1994 11:17 - PRIVATE
  Load the specified combo box with the items in the <txtColl>.
  Set the current selection as the specified input <selIdx> and return
  the selection index.
  Return nil if failed. 
*/
Def initComboBox(self, comboId, txtColl, selIdx)
{
  clearCBText(self, comboId);
  if txtColl cand (size(txtColl) > 0)
    do (txtColl,
      {using(txtElement)
        addCBText(self, comboId, txtElement);
      }); 
    /* Set current selection and focus */  
    ^sendDlgItemMessage(self, comboId, CB_SETCURSEL, selIdx, 0L); 
  endif;
  ^nil;
}
!!

/* 6/2/1994 13:03 - PUBLIC */
Def getAddressSize(self)
{
  ^operandAddrSize;
}
!!

/* Dialog box command processing. */
Def command(self, wp, lp | spaceTxt addrSize status)
{
  select
    case wp = IDCANCEL
      ^end(self, IDCANCEL);
    endCase
     
    case wp = DLG_SLASM_ASSEMBLE
      processAssemble(self);
    endCase
    
    case wp = DLG_SLASM_SKIP
      advanceOffsetToNextStatement(self);
    endCase
    
    case wp = IDHELP
      contextNumber(TheHelpObject, hWnd, helpEntry);
    endCase        
    
  endSelect;
  ^GOOD;
}
!!

/* PUBLIC
 * By returning a 1 from the INITDIALOG message, we
 * are telling MS-Windows to set the input focus to
 * first tabstop item.
 */
Def initDialog(self, wP, lP | spaceColl txtColl addrTxt lines)
{
  registerF1Help(CLIULibraryClass$Inst, HI_INPUT_DLG_WITH_HELP, /* @@ */
    getHWnd(self), helpEntry);
    
  /* place values for the dasm address and source */
  initSourceFields(self);
  
  /* Set the operand/AddressSize */
  disableItem(self, DLG_SLASM_SIZE_COMBO);
  disableItem(self, DLG_SLASM_SIZE_TEXT);
  
  /* Retrieve the address spaces list from the Address Server */
  if not(spaceColl := getAddressSpaces(AddressLibClass$Inst)) cor
    not(spaceColl := processAddressSpaces(self, spaceColl)) cor
    not(initComboBox(self, DLG_SLASM_SPACE_COMBO, spaceColl, 0))
      /* disable the control */
      disableItem(self, DLG_SLASM_SPACE_COMBO);  
  else
    if (addrTxt := getAddrSpaceText(AddressLibClass$Inst, addrSpace))
      sendDlgItemMessage(self, DLG_SLASM_SPACE_COMBO, LB_SELECTSTRING, 
        0, asciiz(addrTxt)); 
    endif;         
  endif;
  
  /* @@ Uncomment if we ever support operand/address size in dialog...  
  // Set the operand/AddressSize
  txtColl := new(OrderedCollection, 3);
  add(txtColl, "Auto");
  add(txtColl, "Use16");
  add(txtColl, "Use32");
  if not(initComboBox(self, DLG_SLASM_SIZE_COMBO, txtColl, operandAddrSize)) 
    cor not(operandAddrSize) then
      disableItem(self, DLG_SLASM_SIZE_COMBO);  
  else
    sendDlgItemMessage(self, DLG_SLASM_SIZE_COMBO, LB_SELECTSTRING, 
      0, asciiz(txtColl[operandAddrSize]));     
  endif;
  ... @@ */
} 
!!
