mirror of
https://github.com/michaelcmartin/Ophis.git
synced 2024-11-05 17:07:33 +00:00
444 lines
9.7 KiB
HTML
444 lines
9.7 KiB
HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
|
<HTML
|
|
><HEAD
|
|
><TITLE
|
|
>A modest example: Insertion sort on linked lists</TITLE
|
|
><META
|
|
NAME="GENERATOR"
|
|
CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK
|
|
REL="HOME"
|
|
TITLE="Programming with Ophis"
|
|
HREF="book1.html"><LINK
|
|
REL="UP"
|
|
TITLE="Structured Programming"
|
|
HREF="c680.html"><LINK
|
|
REL="PREVIOUS"
|
|
TITLE="Data structures"
|
|
HREF="x803.html"><LINK
|
|
REL="NEXT"
|
|
TITLE="Pointers and Indirection"
|
|
HREF="c885.html"></HEAD
|
|
><BODY
|
|
CLASS="SECTION"
|
|
BGCOLOR="#FFFFFF"
|
|
TEXT="#000000"
|
|
LINK="#0000FF"
|
|
VLINK="#840084"
|
|
ALINK="#0000FF"
|
|
><DIV
|
|
CLASS="NAVHEADER"
|
|
><TABLE
|
|
SUMMARY="Header navigation table"
|
|
WIDTH="100%"
|
|
BORDER="0"
|
|
CELLPADDING="0"
|
|
CELLSPACING="0"
|
|
><TR
|
|
><TH
|
|
COLSPAN="3"
|
|
ALIGN="center"
|
|
>Programming with Ophis</TH
|
|
></TR
|
|
><TR
|
|
><TD
|
|
WIDTH="10%"
|
|
ALIGN="left"
|
|
VALIGN="bottom"
|
|
><A
|
|
HREF="x803.html"
|
|
ACCESSKEY="P"
|
|
><<< Previous</A
|
|
></TD
|
|
><TD
|
|
WIDTH="80%"
|
|
ALIGN="center"
|
|
VALIGN="bottom"
|
|
>Structured Programming</TD
|
|
><TD
|
|
WIDTH="10%"
|
|
ALIGN="right"
|
|
VALIGN="bottom"
|
|
><A
|
|
HREF="c885.html"
|
|
ACCESSKEY="N"
|
|
>Next >>></A
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
><HR
|
|
ALIGN="LEFT"
|
|
WIDTH="100%"></DIV
|
|
><DIV
|
|
CLASS="SECTION"
|
|
><H1
|
|
CLASS="SECTION"
|
|
><A
|
|
NAME="AEN856"
|
|
>A modest example: Insertion sort on linked lists</A
|
|
></H1
|
|
><P
|
|
> To demonstrate these techniques, we will now produce code to
|
|
perform insertion sort on a linked list. We'll start by defining
|
|
our data structure, then defining the routines we want to write,
|
|
then producing actual code for those routines. A downloadable
|
|
version that will run unmodified on a Commodore 64 closes the
|
|
chapter.
|
|
</P
|
|
><DIV
|
|
CLASS="SECTION"
|
|
><H2
|
|
CLASS="SECTION"
|
|
><A
|
|
NAME="AEN859"
|
|
>The data structure</A
|
|
></H2
|
|
><P
|
|
> We don't really want to have to deal with pointers if we can
|
|
possibly avoid it, but it's hard to do a linked list without
|
|
them. Instead of pointers, we will
|
|
use <I
|
|
CLASS="EMPHASIS"
|
|
>cursors</I
|
|
>: small integers that represent
|
|
the index into the array of values. This lets us use the
|
|
many-small-byte-arrays technique for our data. Furthermore, our
|
|
random data that we're sorting never has to move, so we may
|
|
declare it as a constant and only bother with changing the
|
|
values of <TT
|
|
CLASS="LITERAL"
|
|
>head</TT
|
|
> and
|
|
the <TT
|
|
CLASS="LITERAL"
|
|
>next</TT
|
|
> arrays. The data record definition
|
|
looks like this:
|
|
</P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
> head : byte;
|
|
data : const int[16] = [838, 618, 205, 984, 724, 301, 249, 946,
|
|
925, 43, 114, 697, 985, 633, 312, 86];
|
|
next : byte[16];</PRE
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
><P
|
|
> Exactly how this gets represented will vary from assembler to
|
|
assembler. Ophis does it like this:
|
|
</P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>.data
|
|
.space head 1
|
|
.space next 16
|
|
|
|
.text
|
|
lb: .byte <$838,<$618,<$205,<$984,<$724,<$301,<$249,<$946
|
|
.byte <$925,<$043,<$114,<$697,<$985,<$633,<$312,<$086
|
|
hb: .byte >$838,>$618,>$205,>$984,>$724,>$301,>$249,>$946
|
|
.byte >$925,>$043,>$114,>$697,>$985,>$633,>$312,>$086</PRE
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><DIV
|
|
CLASS="SECTION"
|
|
><H2
|
|
CLASS="SECTION"
|
|
><A
|
|
NAME="AEN868"
|
|
>Doing an insertion sort</A
|
|
></H2
|
|
><P
|
|
> To do an insertion sort, we clear the list by setting the 'head'
|
|
value to -1, and then insert each element into the list one at a
|
|
time, placing each element in its proper order in the list. We
|
|
can consider the lb/hb structure alone as an array of 16
|
|
integers, and just insert each one into the list one at a
|
|
time.
|
|
</P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>procedure insertion_sort
|
|
head := -1;
|
|
for i := 0 to 15 do
|
|
insert_elt i
|
|
end
|
|
end</PRE
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
><P
|
|
> This translates pretty directly. We'll have insert_elt take its
|
|
argument in the X register, and loop with that. However, given
|
|
that insert_elt is going to be a complex procedure, we'll save
|
|
the value first. The assembler code becomes:
|
|
</P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
; insertion'sort: Sorts the list defined by head, next, hb, lb.
|
|
; Arguments: None.
|
|
; Modifies: All registers destroyed, head and next array sorted.
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
insertion'sort:
|
|
lda #$FF ; Clear list by storing the terminator in 'head'
|
|
sta head
|
|
ldx #$0 ; Loop through the lb/hb array, adding each
|
|
insertion'sort'loop: ; element one at a time
|
|
txa
|
|
pha
|
|
jsr insert_elt
|
|
pla
|
|
tax
|
|
inx
|
|
cpx #$10
|
|
bne insertion'sort'loop
|
|
rts</PRE
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><DIV
|
|
CLASS="SECTION"
|
|
><H2
|
|
CLASS="SECTION"
|
|
><A
|
|
NAME="AEN874"
|
|
>Inserting an element</A
|
|
></H2
|
|
><P
|
|
> The pseudocode for inserting an element is a bit more
|
|
complicated. If the list is empty, or the value we're inserting
|
|
goes at the front, then we have to update the value
|
|
of <TT
|
|
CLASS="LITERAL"
|
|
>head</TT
|
|
>. Otherwise, we can iterate through
|
|
the list until we find the element that our value fits in after
|
|
(so, the first element whose successor is larger than our
|
|
value). Then we update the next pointers directly and exit.
|
|
</P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>procedure insert_elt i
|
|
begin
|
|
if head = -1 then begin
|
|
head := i;
|
|
next[i] := -1;
|
|
return;
|
|
end;
|
|
val := data[i];
|
|
if val < data[i] then begin
|
|
next[i] := head;
|
|
head := i;
|
|
return;
|
|
end;
|
|
current := head;
|
|
while (next[current] <> -1 and val < data[next[current]]) do
|
|
current := next[current];
|
|
end;
|
|
next[i] := next[current];
|
|
next[current] := i;
|
|
end;</PRE
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
><P
|
|
> This produces the following rather hefty chunk of code:
|
|
</P
|
|
><TABLE
|
|
BORDER="0"
|
|
BGCOLOR="#E0E0E0"
|
|
WIDTH="100%"
|
|
><TR
|
|
><TD
|
|
><PRE
|
|
CLASS="PROGRAMLISTING"
|
|
>;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
; insert_elt: Insert an element into the linked list. Maintains the
|
|
; list in sorted, ascending order. Used by
|
|
; insertion'sort.
|
|
; Arguments: X register holds the index of the element to add.
|
|
; Modifies: All registers destroyed; head and next arrays updated
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
.data
|
|
.space lbtoinsert 1
|
|
.space hbtoinsert 1
|
|
.space indextoinsert 1
|
|
|
|
.text
|
|
|
|
insert_elt:
|
|
ldy head ; If the list is empty, make
|
|
cpy #$FF ; head point at it, and return.
|
|
bne insert_elt'list'not'empty
|
|
stx head
|
|
tya
|
|
sta next,x
|
|
rts
|
|
insert_elt'list'not'empty:
|
|
lda lb,x ; Cache the data we're inserting
|
|
sta lbtoinsert
|
|
lda hb,x
|
|
sta hbtoinsert
|
|
stx indextoinsert
|
|
ldy head ; Compare the first value with
|
|
sec ; the data. If the data must
|
|
lda lb,y ; be inserted at the front...
|
|
sbc lbtoinsert
|
|
lda hb,y
|
|
sbc hbtoinsert
|
|
bmi insert_elt'not'smallest
|
|
tya ; Set its next pointer to the
|
|
sta next,x ; old head, update the head
|
|
stx head ; pointer, and return.
|
|
rts
|
|
insert_elt'not'smallest:
|
|
ldx head
|
|
insert_elt'loop: ; At this point, we know that
|
|
lda next,x ; argument > data[X].
|
|
tay
|
|
cpy #$FF ; if next[X] = #$FF, insert arg at end.
|
|
beq insert_elt'insert'after'current
|
|
lda lb,y ; Otherwise, compare arg to
|
|
sec ; data[next[X]]. If we insert
|
|
sbc lbtoinsert ; before that...
|
|
lda hb,y
|
|
sbc hbtoinsert
|
|
bmi insert_elt'goto'next
|
|
insert_elt'insert'after'current: ; Fix up all the next links
|
|
tya
|
|
ldy indextoinsert
|
|
sta next,y
|
|
tya
|
|
sta next,x
|
|
rts ; and return.
|
|
insert_elt'goto'next: ; Otherwise, let X = next[X]
|
|
tya ; and go looping again.
|
|
tax
|
|
jmp insert_elt'loop</PRE
|
|
></TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
><DIV
|
|
CLASS="SECTION"
|
|
><H2
|
|
CLASS="SECTION"
|
|
><A
|
|
NAME="AEN881"
|
|
>The complete application</A
|
|
></H2
|
|
><P
|
|
> The full application, which deals with interfacing with CBM
|
|
BASIC and handles console I/O and such, is
|
|
in <A
|
|
HREF="x1159.html"
|
|
><I
|
|
><I
|
|
>structuredemo.oph</I
|
|
></I
|
|
></A
|
|
>.
|
|
</P
|
|
></DIV
|
|
></DIV
|
|
><DIV
|
|
CLASS="NAVFOOTER"
|
|
><HR
|
|
ALIGN="LEFT"
|
|
WIDTH="100%"><TABLE
|
|
SUMMARY="Footer navigation table"
|
|
WIDTH="100%"
|
|
BORDER="0"
|
|
CELLPADDING="0"
|
|
CELLSPACING="0"
|
|
><TR
|
|
><TD
|
|
WIDTH="33%"
|
|
ALIGN="left"
|
|
VALIGN="top"
|
|
><A
|
|
HREF="x803.html"
|
|
ACCESSKEY="P"
|
|
><<< Previous</A
|
|
></TD
|
|
><TD
|
|
WIDTH="34%"
|
|
ALIGN="center"
|
|
VALIGN="top"
|
|
><A
|
|
HREF="book1.html"
|
|
ACCESSKEY="H"
|
|
>Home</A
|
|
></TD
|
|
><TD
|
|
WIDTH="33%"
|
|
ALIGN="right"
|
|
VALIGN="top"
|
|
><A
|
|
HREF="c885.html"
|
|
ACCESSKEY="N"
|
|
>Next >>></A
|
|
></TD
|
|
></TR
|
|
><TR
|
|
><TD
|
|
WIDTH="33%"
|
|
ALIGN="left"
|
|
VALIGN="top"
|
|
>Data structures</TD
|
|
><TD
|
|
WIDTH="34%"
|
|
ALIGN="center"
|
|
VALIGN="top"
|
|
><A
|
|
HREF="c680.html"
|
|
ACCESSKEY="U"
|
|
>Up</A
|
|
></TD
|
|
><TD
|
|
WIDTH="33%"
|
|
ALIGN="right"
|
|
VALIGN="top"
|
|
>Pointers and Indirection</TD
|
|
></TR
|
|
></TABLE
|
|
></DIV
|
|
></BODY
|
|
></HTML
|
|
> |