As instruções de montagem são (geralmente) um mapeamento direto para os códigos de operação, que são valores de (multi) bytes de código de máquina que podem ser interpretados diretamente pelo processador. É bem possível escrever um programa em código de operação diretamente, pesquisando-os em uma tabela (como esta para o microprocessador 6039 , por exemplo) que os lista com as instruções de montagem correspondentes e determinando manualmente endereços / compensações de memória para coisas como saltos.
Os primeiros programas foram feitos exatamente dessa maneira - opcodes escritos à mão.
No entanto, na maioria das vezes é mais simples usar um assembler para "compilar" o código de montagem, que realiza automaticamente essas pesquisas de código de operação, além de ser útil na computação de endereços / deslocamentos para etiquetas de salto nomeadas etc.
Os primeiros montadores foram escritos à mão. Esses assemblers poderiam então ser usados para montar assemblers mais complicados, que poderiam ser usados para montar compiladores escritos para linguagens de nível superior, e assim por diante. Esse processo de escrita iterativa das ferramentas para simplificar a criação do próximo conjunto de ferramentas é chamado de bootstrap (como mencionado por David Rabinowitz em sua resposta) .