<chapter>
  <title>Advanced Memory Segments</title>

  <para>
    By now we've covered the basics of every command in the assembler;
    in this final installment we show the full capabilities of
    the <literal>.text</literal> and <literal>.data</literal> commands
    as we produce a more sophisticated set of Commodore 64 header
    files.
  </para>

  <section>
    <title>The Problem</title>

    <para>
      Our <literal>print'str</literal> routine
      in <xref linkend="tutor6-src" endterm="tutor6-fname"> accesses
      memory locations $10 and $11 directly.  We'd prefer to have
      symbolic names for them.  This reprises our concerns back in
      <xref linkend="ch5-link"> when we concluded that we wanted two
      separate program counters.  Now we realize that we really need
      three; one for the text, one for the data, and one for the zero
      page data.  And if we're going to allow three, we really should
      allow any number.
    </para>
  </section>
  <section>
    <title>The Solution</title>

    <para>
      The <literal>.data</literal> and <literal>.text</literal>
      commands can take a label name after them&mdash;this names a new
      segment.  We'll define a new segment
      called <literal>zp</literal> (for <quote>zero page</quote>) and
      have our zero-page variables be placed there.  We can't actually
      use the default origin of $0000 here either, though, because the
      Commodore 64 reserves memory locations 0 and 1 to control its
      memory mappers:
    </para>

<programlisting>
.data zp
.org $0002
</programlisting>

    <para>
      Now, actually, the rest of the zero page is reserved too:
      locations $02-$8F are used by the BASIC interpreter, and
      locations $90-$FF are used by the KERNAL.  We don't need the
      BASIC interpreter, though, so we can back up all of $02-$8F at
      the start of our program and restore it all when we're done.
    </para>

    <para>
      In fact, since we're disablng BASIC, we can actually also swap
      out its ROM entirely and get a contiguous block of RAM from
      $0002 to $CFFF:
    </para>

<programlisting>
.scope
        ; Cache BASIC zero page at top of available RAM
        ldx     #$8e
*       lda     $01, x
        sta     $cf81, x
        dex
        bne     -

        ; Swap out the BASIC ROM for RAM
        lda     $01
        and     #$fe
        ora     #$06
        sta     $01

        ; Run the real program
        jsr     _main

        ; Restore BASIC ROM
        lda     $01
        ora     #$07
        sta     $01

        ; Restore BASIC zero page
        ldx     #$8e
*       lda     $cf81, x
        sta     $01, x
        dex
        bne     -

        ; Back to BASIC
        rts

_main:
        ; _main points at the start of the real program,
        ; which is actually outside of this scope
.scend
</programlisting>

    <para>
      Our <literal>print'str</literal> routine is then rewritten to
      declare and use a zero-page variable, like so:
    </para>

<programlisting>
; PRINTSTR routine.  Accumulator stores the low byte of the address,
; X register stores the high byte.  Destroys the values of $10 and
; $11.

.scope
.data zp
.space _ptr 2
.text
printstr:
        sta _ptr
        stx _ptr+1
        ldy #$00
_lp:    lda (_ptr),y
        beq _done
        jsr chrout
        iny
        bne _lp
_done:  rts
.scend
</programlisting>

    <para>
      Also, we ought to put in an extra check to make sure our
      zero-page allocations don't overflow, either:
    </para>

<programlisting>
.data zp
.checkpc $80
</programlisting>

    <para>
      The final source file is <xref linkend="tutor7-src"
      endterm="tutor7-fname">.
    </para>
  </section>
</chapter>
