Skip to content

Instantly share code, notes, and snippets.

@neuro-sys
Last active March 4, 2019 21:36
Show Gist options
  • Save neuro-sys/52204b23c6cd7017d1eba2f8f70bdccc to your computer and use it in GitHub Desktop.
Save neuro-sys/52204b23c6cd7017d1eba2f8f70bdccc to your computer and use it in GitHub Desktop.
BDOS EQU &5
C_WRITESTR EQU &9
; Structs and the member sizes
edge_status equ 1 ; 0 negative, 1 positive
edge_node equ 2 ; pointer to node this edge connects to
edge_next equ 2 ; pointer to next edge
edge_s_offset equ edge_status + edge_node + edge_next
node_name equ 64 ; name
node_edges equ 2 ; pointer to edge vector
node_next equ 2 ; pointer to next node
node_s_offset equ node_name + node_edges + node_next
org &100
di ; Disable interrupts
call clear_data_area ; Clear data area
call initgraph ; Initialize graph with relationships
call isstable ; Check if graph is stable
ei ; Enable interrupts and return
rst 0 ; Return to CCP
initgraph: ld hl, input ; Load input list
initgraph_l3: call getline ; Read only one line into inputbuffer
cp 1 ; Check end of input flag
jp nz, initgraph_l4 ; If the end, exit
ret
initgraph_l4: push hl ; Save new input list pointer
ld hl, inputbuffer ; load input address
call parseinput ; Parse line into first, second and status
ld hl, first ; Load first name
push hl
call node_findbyname ; Search for first name
pop de
ld a, l
cp h
jp nz, initgraph_l1
ex de, hl
call node_append
initgraph_l1: ex de, hl
ld hl, primary
ld (hl), e
inc hl
ld (hl), d ; primary = first
ld hl, second
push hl
call node_findbyname ; Search for second name
pop de
ld a, l
cp h
jp nz, initgraph_l2
ex de, hl
call node_append
initgraph_l2: ex de, hl
ld hl, secondary
ld (hl), e
inc hl
ld (hl), d ; secondary = second
ld hl, primary
ld a, (hl)
inc hl
ld h, (hl)
ld l, a ;
ld de, 64 ; edges offset
add hl, de ;
ex de, hl ; DE = primary node edges
ld hl, secondary
ld a, (hl)
inc hl
ld h, (hl)
ld l, a ; HL = secondary node
ld bc, curstatus
ld a, (bc) ; A = status
call edge_append
ld hl, secondary
ld a, (hl)
inc hl
ld h, (hl)
ld l, a
ld de, 64
add hl, de
ex de, hl
ld hl, primary
ld a, (hl)
inc hl
ld h, (hl)
ld l, a
ld bc, curstatus
ld a, (bc)
call edge_append
pop hl ; Restore input address
jp initgraph_l3 ; Repeat
inputended: ret
; no input, using the graph in node_head
; determines if the graph is balanced or not
; by printing a message to terminal
isstable: ld hl, node_head
ld e, (hl)
inc hl
ld d, (hl)
ex de, hl ; HL = *node_head
isstable_l2: ex de, hl
ld hl, node1
ld (hl), e
inc hl
ld (hl), d ; node1 = *node_head
ex de, hl
ld a, l
cp h
cp 0
jp nz, isstable_l3 ; if node1 == NULL
ld de, msg_balanced
ld c, C_WRITESTR
call BDOS
ret ; print and return
isstable_l3: call isstable2
cp 1
jp nz, isstable_l4 ; Exit if unbalanced
ret
isstable_l4: ld hl, node1
ld e, (hl)
inc hl
ld d, (hl)
ex de, hl ; HL = *node1
ld de, 66
add hl, de ; HL = node1->next
ld e, (hl)
inc hl
ld d, (hl)
ex de, hl
jp isstable_l2
; Inner iteration for isstable routine
; Returns 1 in A if unbalanced condition is found
; Otherwise 0
isstable2: ld hl, node_head
ld e, (hl)
inc hl
ld d, (hl)
ex de, hl ; HL = *node_head
isstable2_l2: ex de, hl
ld hl, node2
ld (hl), e
inc hl
ld (hl), d ; node2 = *node_head
ex de, hl
ld a, l
cp h
jp nz, isstable2_l3 ; if node2 == NULL
xor a
ret ; End of iteration, return
isstable2_l3: ld hl, node1
ld a, (hl)
inc hl
ld h, (hl)
ld l, a
ex de, hl ; DE = *node1
ld hl, node2
ld a, (hl)
inc hl
ld h, (hl)
ld l, a ; HL = *node2
call strcmp
cp 0
jp z, isstable2_l5 ; If node1->name == node2->name, skip
call isstable3
cp 1
jp nz, isstable2_l5
ret
isstable2_l5: ld hl, node2
ld a, (hl)
inc hl
ld h, (hl)
ld l, a ; HL = *node2
ld de, 66
add hl, de ; HL = node2->next
ld e, (hl)
inc hl
ld d, (hl)
ex de, hl
jp isstable2_l2
; second level inner loop of isstable
; Print unbalanced if an unbalanced condition is met
; And returns 1 in A
; Otherwise 0
isstable3: ld hl, node_head
ld e, (hl)
inc hl
ld d, (hl)
ex de, hl ; HL = *node_head
isstable3_l2: ex de, hl
ld hl, node3
ld (hl), e
inc hl
ld (hl), d ; node3 = *node_head
ex de, hl
ld a, l
cp h
jp nz, isstable3_l3 ; if node3 == NULL, then end of iteration
xor a
ret
isstable3_l3: ld hl, node1
ld a, (hl)
inc hl
ld h, (hl)
ld l, a
ex de, hl ; DE = *node1
ld hl, node3
ld a, (hl)
inc hl
ld h, (hl)
ld l, a ; HL = *node3
call strcmp
cp 0
jp z, isstable3_l4 ; If node1->name == node3->name, skip
ld hl, node2
ld a, (hl)
inc hl
ld h, (hl)
ld l, a
ex de, hl ; DE = *node2
ld hl, node3
ld a, (hl)
inc hl
ld h, (hl)
ld l, a ; HL *node3
call strcmp
cp 0
jp z, isstable3_l4 ; If node2->name == node3->name, skip
; Do body
call is_all_friends
push af
call common_enemy
pop hl
or h ; if is_all_friends || common_enemy
jp nz, isstable3_l4 ; continue searching, otherwise
ld c, C_WRITESTR
ld de, msg_unbalanced
call BDOS
ld a, 1 ; print message, and return with 1
ret
isstable3_l4: ld hl, node3
ld e, (hl)
inc hl
ld d, (hl)
ex de, hl ; HL = node3
ld de, 66
add hl, de ; HL = node3->next
ld e, (hl)
inc hl
ld d, (hl)
ex de, hl
jp isstable3_l2
; A = 1 if node1, node2, node3 are all friends
is_all_friends: ld hl, node1
ld e, (hl)
inc hl
ld d, (hl) ; DE = *node1
ld hl, node2
ld c, (hl)
inc hl
ld b, (hl) ; BC = *node2
ex de, hl ; HL = *node1
push hl
call node_friends ; node1 and node2
pop de
cp 0
ret z
ld hl, node3
ld c, (hl)
inc hl
ld b, (hl) ; BC = *node3
ex de, hl ; HL = *node1
push bc
call node_friends ; node1 and node3
pop de
cp 0
ret z
ld hl, node2
ld c, (hl)
inc hl
ld b, (hl) ; BC = *node2
ex de, hl ; HL = *node3
call node_friends ; node2 and node3
cp 0
ret z
ret
; A = 1 if among node1, node2, and node3, a pair considers the other an enemy
common_enemy: ld hl, node3
ld c, (hl)
inc hl
ld b, (hl) ; BC = node3
ld hl, node2
ld e, (hl)
inc hl
ld d, (hl) ; DE = node2
ld hl, node1
ld a, (hl)
inc hl
ld h, (hl)
ld l, a ; HL = node1
push hl
call node_enemy
pop de
cp 1
ret z
common_enem1: ld hl, node2
ld c, (hl)
inc hl
ld b, (hl) ; BC = node2, DE = node1
ld hl, node3
ld a, (hl)
inc hl
ld h, (hl)
ld l, a ; HL = node3
push de
call node_enemy
pop bc
cp 1
ret z
ld hl, node3
ld e, (hl)
inc hl
ld d, (hl) ; DE = node3, BC = node1
ld hl, node2
ld a, (hl)
inc hl
ld h, (hl)
ld l, a
call node_enemy
cp 1
ret z
ret
; HL = input
; inputbuffer will have zero terminated string
; HL will have next input pointer after it's advanced
; A = 1 if finished
getline: ld de, inputbuffer ; Load dest buffer
ld a, (hl) ; Check first char
cp &13 ; Is it end?
jp nz, getline_l1 ; If not, continue
ld a, 1 ; otherwise, set end flag, return
ret
getline_l1: cp 0
jp nz, getline_l2
halt
getline_l2: call strcpy
ld a, 0
ret
; HL = input buffer
; first, second, and status fields are set via inputbuffer
parseinput: ld a, (hl) ; check cur char
cp '+' ; Is it '+'?
jp z, hitmarker
cp '-' ; Is it '-'?
jp z, hitmarker
inc hl ; next char
jp parseinput ; loop
hitmarker: cp '+' ; Is it '+'?
ex de, hl ; DE = cur input ptr
ld hl, curstatus ; HL = cur status
jp nz, hitpositive ; If not positive
ld (hl), 1 ; otherwise set cur status as 1
jp markerdone
hitpositive: ld (hl), 0 ; if positive, set as 0
markerdone: ex de, hl ; HL = cur input ptr
push hl ; Save cur input ptr
dec hl ; One before the marker
or a
ld de, inputbuffer ; Load beginning of input buffer
sbc hl, de ; size = cur input ptr - input begin
ld c, l
ld b, h ; bc = size
ex de, hl ; hl = input begin
ld de, first ; de = first buffer
ldir ; copy
ex de, hl ; hl = last char
ld (hl), 0 ; put string terminator
pop hl ; Restore last cur input ptr
inc hl ; skip the second marker
inc hl ; skip white space
inc hl ; Second name
push hl ; Save second name begin
parseinput2: ld a, (hl) ; Load char
cp 0 ; Is it string terminator?
jp z, second_done
inc hl ; otherwise, next char
jp parseinput2 ; loop
second_done: pop de ; Restore second name begin
or a
sbc hl, de ; cur ptr - begin
ld c, l
ld b, h ; bc = size
ex de, hl ; hl = second name begin
ld de, second ; second buffer
ldir ; copy
ex de, hl ; hl = last char
ld (hl), 0 ; put string terminator
ret
; HL = node1
; DE = node2
; BC = enemy
; out A = 1 if BC is a common enemy of node1 and node2
; 0 otherwise
node_enemy: push bc
push de
ld de, 64 ; offset to edges list
add hl, de
ld a, (hl)
inc hl
ld h, (hl)
ld l, a
ex de, hl ; DE = node1->edges
ld hl, edge1
ld (hl), e
inc hl
ld (hl), d ; edge1 = node1->edges
pop hl ; HL = node2
ld de, 64 ; offset to edges list
add hl, de
ld a, (hl)
inc hl
ld h, (hl)
ld l, a
ex de, hl ; DE = node2->edges
ld hl, edge2_begin
ld (hl), e
inc hl
ld (hl), d ; edge2_begin = node2->edges
pop de ; DE = enemy
ld hl, enemy
ld (hl), e
inc hl
ld (hl), d ; enemy = BC
call node_enem1 ; now &edge1, &edge2_begin, enemy set
ret
; inner loop of node_enemy, works on edge1, edge2 and enemy objects
node_enem1: ld hl, edge1
ld a, (hl)
inc hl
ld h, (hl)
ld l, a ; HL = *edge1
node_enem1_l3: ld a, l
cp h
jp nz, node_enem1_l2 ; if *edge1 == NULL
xor a ; not found, return
ret
node_enem1_l2: ld hl, edge2_begin
ld e, (hl)
inc hl
ld d, (hl) ; DE = *edge2_begin
ld hl, edge2
ld (hl), e
inc hl
ld (hl), d ; edge2 = DE
call node_enem2
cp 0
jp z, node_enem1_l4
ret
node_enem1_l4: ld hl, edge1
ld a, (hl)
inc hl
ld h, (hl)
ld l, a
inc hl
inc hl
inc hl ; edge next offset
ld a, (hl)
inc hl
ld h, (hl)
ld l, a
ex de, hl
ld hl, edge1
ld (hl), e
inc hl
ld (hl), d ; edge1 = &edge1->next
ex de, hl
jp node_enem1_l3
; second inner loop of node_enemy, works on edge1, edge2 and enemy objects
; returns 1 in A if conditions are met, 0 otherwise
node_enem2: ld hl, edge2
ld a, (hl)
inc hl
ld h, (hl)
ld l, a ; HL = *edge2
node_enem2_l3: ld a, l
cp h
jp nz, node_enem2_l2 ; if *edge2 == NULL
xor a ; not found, return
ret
node_enem2_l2: ld hl, edge1
ld a, (hl)
inc hl
ld h, (hl)
ld l, a ; HL = *edge1
ld a, (hl) ; Load status
cp 1 ; Is it a friend of edge1?
jp z, node_enem2_l4 ; if yes, then skip
ld hl, edge2
ld a, (hl)
inc hl
ld h, (hl)
ld l, a ; HL = *edge2
ld a, (hl) ; Load status
cp 1 ; Is it a friend of edge2?
jp z, node_enem2_l4 ; if yes, then skip
ld hl, edge1
ld a, (hl)
inc hl
ld h, (hl)
ld l, a
inc hl ; edge1->node offset
ld a, (hl)
inc hl
ld h, (hl)
ld l, a ; HL = *edge1->node
push hl
ld hl, edge2
ld a, (hl)
inc hl
ld h, (hl)
ld l, a
inc hl ; edge2->node offset
ld a, (hl)
inc hl
ld h, (hl)
ld l, a ; HL = edge2->node
pop de ; DE = edge1->node
push de
call strcmp
pop de
cp 0
jp nz, node_enem2_l4 ; if not shared then skip
ld hl, enemy
ld a, (hl)
inc hl
ld h, (hl)
ld l, a ; HL = enemy
call strcmp
cp 0
jp nz, node_enem2_l4 ; if not enemy, then skip
ld a, 1
ret
node_enem2_l4: ld hl, edge2
ld a, (hl)
inc hl
ld h, (hl)
ld l, a
inc hl
inc hl
inc hl ; edge next offset
ld a, (hl)
inc hl
ld h, (hl)
ld l, a
ex de, hl
ld hl, edge2
ld (hl), e
inc hl
ld (hl), d ; edge2 = &edge2->next
ex de, hl
jp node_enem2_l3
; HL = node1
; BC = node2
; out A = 1 if friends
; 0 if not
node_friends: ld de, 64 ; edges offset
add hl, de ; HL = node1->edges
ld e, (hl)
inc hl
ld d, (hl)
ex de, hl ; HL = *node1->edges
nodefri_l2: ld a, l
cp h
jp nz, nodefri_l3 ; if *node1->edges == NULL
ld a, 0 ; no friends found
ret
nodefri_l3: push hl
push bc
inc hl ; node offset
ld e, (hl)
inc hl
ld d, (hl)
ex de, hl ; HL = edge->node
ld e, c
ld d, b ; BC = node2
call strcmp
pop bc
pop hl
jp nz, nodefri_l4
ld a, 1
ret
nodefri_l4: inc hl
inc hl
inc hl ; next offset
ld e, (hl)
inc hl
ld d, (hl)
ex de, hl ; edge = edge->next
jp nodefri_l2
; HL returns free slot for node
; @TODO if implement node_free, `node_cur_free` should be changed
; To point to the new free slot
node_new: ld hl, node_cur_free ; load next free slot address
ld a, (hl)
cp 0 ; Is it empty?
jp nz, node_new_l2 ; If not zero, find an empty one
inc hl
ld a, (hl)
dec hl
cp 0
jp nz, node_new_l2 ; Check hibyte too
ld de, node_heap ; Otherwise, initialize node_cur_free
ld (hl), e ; with first block in node_heap
inc hl
ld (hl), d ; *node_cur_free = node_heap
ex de, hl ; HL = *node_cur_free
ret
node_new_l2: inc hl
ld h, (hl)
ld l, a ; HL = *node_cur_free
ld de, node_s_offset ; load the size of node entry
node_new_l1: add hl, de ; find next slot
ld a, 0 ; Is it empty?
cp (hl)
jp nz, node_new_l1 ; If not, then keep searching
inc hl
cp (hl)
dec hl
jp nz, node_new_l1 ; If not, then keep searching
node_new_l3: ex de, hl ; Empty found, DE = empty slot in heap
ld hl, node_cur_free
ld (hl), e
inc hl
ld (hl), d ; *node_cur_free = empty slot
dec hl
ex de, hl ; HL = empty slot
ret
; HL returns free slot for edge
; @TODO if implement edge_free, `edge_cur_free` should be changed
; To point to the new free slot
edge_new: ld hl, edge_cur_free ; load next free slot address
ld a, (hl)
cp 0 ; Is it empty?
jp nz, edge_new_l2 ; If not zero, find an empty one
inc hl
ld a, (hl)
dec hl
cp 0
jp nz, edge_new_l2 ; check hibyte
ld de, edge_heap ; Otherwise, initialize edge_cur_free
ld (hl), e ; with first block in edge_heap
inc hl
ld (hl), d ; *edge_cur_free = edge_heap
ex de, hl ; HL = *edge_cur_free
ret
edge_new_l2: inc hl
ld h, (hl)
ld l, a ; HL = *edge_cur_free
ld de, edge_s_offset ; load the size of edge entry
edge_new_l1: add hl, de ; find next slot
ld a, 0 ; Is it empty?
cp (hl)
jp nz, edge_new_l1 ; If not, then keep searching
inc hl
cp (hl)
dec hl
jp nz, edge_new_l1 ; check hibyte
edge_new_l3: ex de, hl ; Empty found, DE = empty slot in heap
ld hl, edge_cur_free
ld (hl), e
inc hl
ld (hl), d ; *edge_cur_free = empty slot
dec hl
ex de, hl ; HL = empty slot
ret
; in HL = name
; out HL = new node address
node_append: push hl ; Save name address
call node_new
ex de, hl ; DE = new node entry
pop hl ; HL = name (first entry)
push de ; Save node address
call strcpy ; Copy name to node.name
node_appendl1: ld hl, node_head ; Load node_head pointer to pointer
ld a, (hl)
inc hl
ld h, (hl)
ld l, a ; HL = *node_head
ld a, 0
cp l ; Is the pointer null?
jp nz, node_appendl5 ; Then initialize node_head
cp h
jp nz, node_appendl5 ; check hibyte
ld hl, node_head ; Load node_head pointer to pointer
pop de ; DE = node address
ld (hl), e
inc hl
ld (hl), d ; *node_head = node_entry
ex de, hl ; HL = node_entry, and return
ret
; Otherwise, find tail via next ptr
node_appendl5: ld de, 66 ; offset to next ptr in struct
add hl, de ; Find address of next ptr
ld a, (hl) ; Load ptr low byte
cp 0 ; Is it null?
jp nz, node_appendl4 ; next is null, so it's tail
inc hl
ld a, (hl) ; check upper byte as well
dec hl
jp nz, node_appendl4
pop de ; DE = node address
ld (hl), e
inc hl
ld (hl), d ; it->next = node
ex de, hl ; Return new node address
ret
node_appendl4: inc hl
ld h, (hl)
ld l, a ; HL = next ptr
jp node_appendl5 ; keep searching
; A = status
; HL = node
; DE = head edge
; out HL = new edge
edge_append: push af
push hl
push de
call edge_new
ld c, l
ld b, h ; BC = new edge
pop de ; DE = head
pop hl ; HL = node
pop af ; A = status
ld (bc), a ; Set status on edge
inc bc ; edge->node
ld a, l
ld (bc), a
inc bc
ld a, h
ld (bc), a ; edge->node = node
dec bc
dec bc ; BC = edge
ex de, hl ; HL = head
ld a, (hl)
cp 0
jp nz, edge_app1
inc hl
ld a, (hl)
dec hl
cp 0
jp nz, edge_app1 ; init head and return
ld (hl), c
inc hl
ld (hl), b ; *head = edge
ld l, c
ld h, b ; HL = edge
ret
edge_app1: ld a, (hl)
inc hl
ld h, (hl)
ld l, a ; HL = cur edge
ld a, l
cp h
jp nz, edge_app3 ; if not null then continue
edge_app2: ld (hl), c
inc hl
ld (hl), b ; edge->next = edge
ld l, c
ld h, b ; HL = edge
ret
edge_app3: inc hl
inc hl
inc hl ; edge->next
ld a, (hl)
cp 0
jp nz, edge_app4
inc hl
ld a, (hl)
dec hl
cp 0
jp nz, edge_app4 ; Loop until edge->next is NULL
jp edge_app2 ; Found empty next, set it and return
edge_app4: ld a, (hl)
inc hl
ld h, (hl)
ld l, a
jp edge_app3
; HL = name
node_findbyname:ex de, hl
ld hl, node_head
ld a, (hl)
cp 0
jp nz, node_findby1 ; head is not initialized
inc hl
ld a, (hl)
dec hl
cp 0
jp nz, node_findby1 ; check hibyte
ld hl, 0
ret
node_findby1: inc hl
ld h, (hl)
ld l, a ; HL = first node
node_findby3: push hl
push de
call strcmp ; Compare node->name to searched name
pop de
pop hl
jp nz, node_findby2
ret ; Return cur node
node_findby2: ld bc, 66
add hl, bc
ld a, (hl)
cp 0
jp nz, node_findby4 ; next ptr is NULL
ld hl, 0
ret
node_findby4: inc hl
ld h, (hl)
ld l, a ; HL = next node
jp node_findby3
; HL = src, null terminated string
; DE = dest
strcpy: ld a, (hl)
ld (de), a
or a
inc hl
inc de
jp nz, strcpy
ret
; HL = src, null terminated string
; DE = dest
; A = 0 if equal
strcmp:
ld b, (hl) ; Load source char
ld a, (de) ; Load dest char
cp b ; compare both chars
jp nz, strcmp2 ; If not equal
xor a
cp b
jp z, strcmp3
inc hl ; Next src char
inc de ; Next dst char
jp strcmp
strcmp2: ld a, 1 ; return 1
ret
strcmp3: ld a, 0
ret
clear_data_area:ld hl, data_area_begin
ld bc, data_area_end
xor a
clear_data_are1:ld (hl), a
inc hl
or a
sbc hl, bc
add hl, bc
jp nz, clear_data_are1
ret
data_area_begin equ $
first: ds 64 ; first name in input line parsed
second: ds 64 ; second name
curstatus: ds 1 ; 1 if inputline has '++', 0 if '--'
primary: ds 2 ; Primary node
secondary: ds 2 ; Secondary node
edge_heap: ds 128*edge_s_offset; Edge objects heap
edge_cur_free: ds 2 ; Points to the next free block
node_heap: ds 32*node_s_offset;
node_cur_free: ds 2 ; The last free node
node_head: ds 2 ; holds node list head
node1: ds 2
node2: ds 2
node3: ds 2
edge2_begin: ds 2 ; needed to reset iterator
edge1: ds 2 ; used by iterators
edge2: ds 2
enemy: ds 2
inputbuffer: ds 256 ; Holds a single inputline
data_area_end equ $
input:
db "Superman ++ Green Lantern", 0
db "Superman ++ Wonder Woman", 0
db "Superman -- Sinestro", 0
db "Superman -- Cheetah", 0
db "Superman -- Lex Luthor", 0
db "Green Lantern ++ Wonder Woman", 0
db "Green Lantern -- Sinestro", 0
db "Green Lantern -- Cheetah", 0
db "Green Lantern -- Lex Luthor", 0
db "Wonder Woman -- Sinestro", 0
db "Wonder Woman -- Cheetah", 0
db "Wonder Woman -- Lex Luthor", 0
db "Sinestro ++ Cheetah", 0
db "Sinestro ++ Lex Luthor", 0
db "Cheetah ++ Lex Luthor", 0
db &13
msg_balanced: db "balanced", &d, &a, '$'
msg_unbalanced: db "unbalanced", &d, &a, '$'
ds &100 ; Extra space for disk image
@neuro-sys
Copy link
Author

balanced
unbalanced

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment