Home-Built Z-80 Computer

De Wikijoan
Dreceres ràpides: navegació, cerca

Contingut

Introducció

NOTA: segueixo el projecte:

Simplest possible homebrew Z80 8-bit computer:

Però es comenten altres projectes


Components

Jo tenia experiència amb la memòria 24LC256 (que té 8 pins). És una memòria de 256Kb = 32KBx8bits, amb 3 pins (A0 A1 A2) per llegir o escriure les dades, però es direcciona amb el protocol I2C ( 2-wire serial interface bus), els pins SCL i SDA. Però això no és el que jo vull. El que jo vull és un xip on pugui direccionar els 10 bits del bus de direccions, i els 8 bits del bus de dades del 80, i aquest és el 28C16, que té 24 pins (DIP24).

Compres

11,53e (1 unitat)

7e (10 unitats)

7e (2 unitats)

12e (1 unitat)

Necessito sockets per la memòria.1 per al programador, i una altra per la placa. 10 x 24-pin Dip / Dil Pcb Ic Socket: 7e (2 unitats)

74HC14: Hex Schmitt Trigger Inverter

870047-DI1.gif

Circuito Integrado Hex Schmitt Trigger Inverter DIP14

El Schmitt Trigger usa la histéresis para prevenir el ruido que podría tapar a la señal original y que causaría falsos cambios de estado si los niveles de referencia y entrada son parecidos.

El schmitt trigger hace uso de la histéresis, que es la tendencia a conservar el nivel lógico hasta que no se produzca un cambio brusco. De esta manera se previene el ruido que podría tapar a la señal original y que causaría falsos cambios de estado si los niveles de referencia y entrada son parecidos.

74HC574N: Octal D-type flip-flop, positive edge-trigger, 3-state

Flip-flop-tipo-d-74hc574.jpg

Són Flip-Flops tipus D. N'hi ha 8. La transició es fa quan el clock passa de low a high. Hi ha un pin per ficar la sortida a alta impedància (Z) i deshabilitar.

The typical use for such a chip is in a microprocessor system to gate the system databus into an I/O device only when the specific I/O address is decoded.

The 74HC574; 74HCT574 is an 8-bit positive-edge triggered D-type flip-flop with 3-state outputs. The device features a clock (CP) and output enable (OE) inputs. The flip-flops will store the state of their individual D-inputs that meet the set-up and hold time requirements on the LOW-to-HIGH clock (CP) transition. A HIGH on OE causes the outputs to assume a high-impedance OFF-state. Operation of the OE input does not affect the state of the flip-flops. Inputs include clamp diodes. This enables the use of current limiting resistors to interface inputs to voltages in excess of VCC.

The D flip-flop is widely used. It is also known as a "data" or "delay" flip-flop. The D flip-flop captures the value of the D-input at a definite portion of the clock cycle (such as the rising edge of the clock). That captured value becomes the Q output. At other times, the output Q does not change.[22][23] The D flip-flop can be viewed as a memory cell, a zero-order hold, or a delay line.

Truth table:

Clock 	        D 	Qnext
Rising edge 	0 	0
Rising edge 	1 	1
Non-Rising 	X 	Q

Memòria 28C16 EEPROM 2KBx8 = 16Kb (CMOS)

28c16.gif

NOTA. Les memòries de 64K (28C64) són més fàcils de trobar en el eBay, i més barates. 28C32 no existeix. La compatibilitat de pins entre 28C16 i 28C64 és quasi!

Fer el programador per gravar la informació en aquesta EPROM està discutit aquí: Programador_EEPROM_amb_Arduino

Construcció. PCB amb CNC, doble capa

v2, hi ha un error

Parteixo del projecte original (carpeta z80micro1-rev1/), i retoco la pcb (_v2) per tal de fer més gruixudes les pistes, fins allà on pugui. A més, faré tres ponts, i així elimino pistes que eren molt llargues i primes. És una PCB de doble capa, i els forats coincideixen perfectament, fent 4 forats de referència amb la broca de 1mm (i filferro de 1mm).

En el v2 he fet un error, i és que partint del projecte original em vaig carregar les línies de contorn, i que en realitat és un poligon. Això vol dir que en el resultat final no m'ha sortit el auto ratsnest, és a dir que tots els grounds estan desconnectats entre sí. Malament. Jo he d'obtenir unes plaques amb unes superfícies de ground ben grosses, tant en el top com en el bottom. No cal que tots els plates del ground estiguin connectats entre si (en el top i en el bottom), sinó que han d'estar connectats entre el top i el bottom. En qualsevol cas, és fàcil veure la continuïtat de tot el GND.

Per fer el ratsnest i els GND està explicat a Eagle_CadSoft#Ratsnest.2C_fer_el_GND_ben_gros

v3. He fet el ratsnest. He fresat 3 plaques. En la primera em vaig equivocar al fer la volta, els drills no coincidien. Això ja ho tinc solucionat. Però aquesta és la que he aconseguit una més bona definició de les pistes primes (pistes de 16, tot i que en la v4 les he fet de 24). He tingut problemes amb les broques V-shape de 10º, que tenen tendència a trencar-se. Aquestes proves les he fet amb la pcb2gcode amb una profunditat de 0,3mm (el valor per defecte és de 0,2mm), i això fa que es mengi una mica més de coure. La última prova és la vàlida, tot i que m'ha quedat unes pistes massa primes (les de 16), i puc tenir problemes al soldar.

v4 (TBD). He implementat vàries millores. He eliminat les pistes de 16mils i les he ficat a 24mils (les pistes que passen entre els pins del xip, 24mils és suficient). He comprat unes broques de 20º, que espero que no es trenqui com les de 10º, i tindran més precisió que les de 30º. En el pcb2gcode he ficat depth=0,2mm (valor per defecte) en comptes de 0,3mm. Es menjarà menys coure i les pistes hauran de quedar més bé, tot i que hauré de repassar detingudament el resultat.

v5a. He fet servir mètode de la planxa i àcid. Ha anat força, el mètoe de la planxa eś eficient, tot i que he malmès la placa. He fet servir una combinació de CNC i planxa que s'ha demostrat bastant efectiva. Primer faig els drills de la placa (amb els 4 forats de referència, també a 0,65-0,7mm). Aleshores amb el mètode de la planxa faig les dues cares. Trec el paper (difícil saber quan has de parar), i ataco amb àcid i aigua oxigenada (correcta, però difícil saber quan has de parar. Si et passes, poden quedar les pistes massa primes). De totes maneres, encara que he malmès la placa, és una bona opció.

v5b. És la primera versió que he construït i ha funcionat (fent servir CNC). He tingut problemes amb les soldadures fredes. D'una banda, tinc un soldador de punta fina de 11 W que va bé, però les soldadures són massa fredes. D'altra banda, tinc soldadors de 26W i 60W però les puntes són massa gruixudes (tot i que en algunes pistes ja és suficient). He corregit un parell d'errors. Hi havia una soldadura freda en la senyal D3, en una soldadura que quedava molt amagada.

He demanat un soldador de punta fina de 26W.

No oblidar-se de soldar les pistes del top que estan unides als xips. He tingut problemes per soldar les pistes del TOP amb els sòcals dels xips (si no faig servir sòcal sinó el xip directament, aleshores no ha de representar cap problemsa). Hi ha dues solucions.

2. L'altra solució és fer servir sòcals tornejats, un soldador de punta ben fina i fil prim. Ens hem d'assegurar que la connexió sigui bona i segura. Amb sòcals tornejats podem accedir al sòcal per fer la soldadura. Si no tenim sòcals tornejats també es pot fer, s'ha d'entrar només la meitat del sòcal per tal de fer una mica d'espai per fer la soldadura, però és més difícil.


En la v5 (he fet vàries proves) finalment he aconseguit una bona placa amb broca V-shape de 20 graus, i el gruix de les pistes òptim està explicat a: Fer_plaques_PCB_amb_màquina_CNC#Gruixos_de_les_pistes_per_a_CNC

v6 (TODO). Treure els ponts i les vies. Això vol dir soldar amb més precisió amb un bon soldador de punta fina. sòcals tornejats.

RESET

RESET must be active for a minimum of three full clock cycles before a reset operation is complete. Al principi semblava que no funcionava el RESET amb el manual clock, però llegint la documentació està clar. Amb el RESET apretat, clicar tres vegades el botó de clock.

Ensamblador Z80. Z80 Assembler

(carpeta projectes/Z80)

z80asm-1.8.tar.gz

$ ./z80asm --help
Usage: ./z80asm [options] [input files]

Possible options are:
-h	--help		Display this help text and exit.
-V	--version	Display version information and exit.
-v	--verbose	Be verbose.  Specify again to be more verbose.
-l	--list		Write a list file.
-L	--label		Write a label file.
-p	--label-prefix	prefix all labels with this prefix.
-i	--input		Specify an input file (-i may be omitted).
-o	--output	Specify the output file.
-I	--includepath	Add a directory to the include path.
Please send bug reports and feature requests to <shevek@fmf.nl>

Els primers codis que vull compilar i gravar en la EEPROM són els que es mostren en el projecte del mini-ordinador Z80:

exemple1.asm:

org $0
        ld a, 1
loop:   out (255), a
        rlca
        jr loop

Per obtenir el binari:

$ ./z80asm -I /headers/ -o examples/exemple1.bin -i examples/exemple1.asm
$ od -Ax -t x1 exemple1.bin
000000 3e 01 d3 ff 07 18 fb
000007

Aquests valors també els puc veure amb la utilitat gràfica ghex

Z80Pack: ensamblador i simulador

És un altre ensamblador, que a més té simulador:

Descarrego la versió 1.34. Les carpetes z80asm/ i z80sim/ es compilen amb make sense problemes.

Per otenir el binari dels meus dos exemples he de tabular el ORG (o senzillament no posar-lo), i sense el símbol del dòllar:

        ORG 0
        ld a, 1
loop:   out (255), a
        rlca
        jr loop

i obtinc el mateix binari

./z80asm -fb exemple1.asm
$ od -Ax -t x1 exemple1.bin
000000 3e 01 d3 ff 07 18 fb
000007

Si compilo sense l'opció -fb obtenim:

$ ./z80asm exemple1b.asm
Z80 - Assembler Release 1.7, Copyright (C) 1987-2016 by Udo Munk
Pass 1
   Read    exemple1b.asm
Pass 2
   Read    exemple1b.asm
0 error(s)
$ od -Ax -t x1 exemple1b.bin
000000 ff 00 00 3e 01 d3 ff 07 18 fb
00000a

I això té la seva importància perquè és aquest fitxer el que va bé per al simulador.

Simulador Z80

$ cd ~/z80pack-1.34/z80sim/srcsim
$ make
$ ./z80sim -z
>>>

Aleshores ja podem carregar el programa amb l'opció r, i és important veure com START:0000 (això ho hem aconseguit compilant sense l'opció -fb):

Un cop estem en el prompt del simulador (>>>), l'opció g és per arrencar el programa, i amb el ENTER (single step program) vaig avançant pel programa. Amb l'opció t, trace puc veure vàries instruccions a l'hora.

$ ./z80sim -z

#######  #####    ###            #####    ###   #     #
     #  #     #  #   #          #     #    #    ##   ##
    #   #     # #     #         #          #    # # # #
   #     #####  #     #  #####   #####     #    #  #  #
  #     #     # #     #               #    #    #     #
 #      #     #  #   #          #     #    #    #     #
#######  #####    ###            #####    ###   #     #

Release 1.34, Copyright (C) 1987-2017 by Udo Munk

CPU speed is unlimited
>>> r exemple1b.bin
Loader statistics for file exemple1b.bin:
START : 0000
END   : 0006
LOADED: 0007

>>> 

PC   A  SZHPNC I  IFF BC   DE   HL   A'F' B'C' D'E' H'L' IX   IY   SP
0002 01 000000 00 00  0000 0000 0000 0000 0000 0000 0000 0000 0000 ffff
OUT	(FF),A
>>> 

PC   A  SZHPNC I  IFF BC   DE   HL   A'F' B'C' D'E' H'L' IX   IY   SP
0004 01 000000 00 00  0000 0000 0000 0000 0000 0000 0000 0000 0000 ffff
RLCA
>>> 

PC   A  SZHPNC I  IFF BC   DE   HL   A'F' B'C' D'E' H'L' IX   IY   SP
0005 02 000000 00 00  0000 0000 0000 0000 0000 0000 0000 0000 0000 ffff
JR	0002
>>> 

PC   A  SZHPNC I  IFF BC   DE   HL   A'F' B'C' D'E' H'L' IX   IY   SP
0002 02 000000 00 00  0000 0000 0000 0000 0000 0000 0000 0000 0000 ffff
OUT	(FF),A
>>> 

Fixem-nos que encara que hem afegit tres bytes al principi del fitxer (ff 00 00), quan fem dump de la memòria (opció -d) aquests tres bytes no apareixen, per tant ho fem bé:

>>> ? -> per veure les opcions

>>> d
Adr    00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f  ASCII
0000 - 3e 01 d3 ff 07 18 fb ec 29 cd ba ab f2 fb e3 46 	>.......)......F

Per visualitzar o canviar un registre, ho fem amb x:

>>> x a
A = 04 : 01

compte: el nom del registre en minúscules. Hem de posar el nou valor, si és el mateix, repetim el valor.

Amb tot això, aquest simulador m'haurà d'anar bé per programar l'algorisme de les Torres de Hanoi.

Programes de prova. Ensamblador Z80

En aquest projecte es proposen dos exercicis simples, on la sortida són 8 LEDs. En el primer exemple els LEDs es van desplaçant, quan arriba al final torna al principi. En el segon exemple, quan el led encès arriba al final, canvia de direcció-

exemple 1:

org $0
        ld a, 1
loop:   out (255), a
        rlca
        jr loop

ORG is an abbreviation for "origin". ORG is merely an indication on where to put the next piece of code/data, related to the current segment. ORG it's the location in memory where you want the binary program to be loaded to, if any. ORG is used to set the assembler location counter.

The LD instruction is used to put the value from one place into another place. Per tant, fiquem un 1 en el registre A (registre general)

És un label, al qual podem cridar dins del mateix loop. Per tant, ens servirà per fer bucles. El programa s'executarà sense fi.

OUT: Writes the value of the second operand into the port given by the first operand.

En el registre A hi havíem ficat un 1. En aquest cas, què significa el port (255)? La idea que es persegueix és que el contingut de A es correspon amb l'encesa dels LEDs.

Llegint en el Z80 CPU User Manual, pàgina 306. OUT (n), A. The operand n is placed on the bottom half (A0 through A7) of the address bus to select the I/O device at one of 256 possible ports. The contents of the Accumulator (Register A) also appear on the top half (A8 through A15) of the address bus at this time. Then the byte contained in the Accumulator is placed on the data bus and written to the selected peripheral device.

Per tant, en el bus de direccions (A0...A7) hem de veure FF (que és el port, però que per nosaltres no té cap sentit), i el contingut de l'acumuladror l'hem de veure en el bus de dades (que és el que interessa), i també a A8...A15 (tot i que nosaltres només tenim A8, A9 i A10. Com que fiquem el contingut de l'acumulador en el bus de dades, això és una operació d'escriptura (WR). Si ens fixem en l'esquema, el clock del LATCH està unit al WR del Z80. Per tant, cada vegada que fem una operació d'escriptura en el bus de dades es fa un latch del bus de dades.

Performs RLC A much quicker, and modifies the flags differently. S,Z, and P/V are preserved, H and N flags are reset.

RLC: 8-bit rotation to the left. The bit leaving on the left is copied into the carry, and to bit 0. Per tant, amb RLCA el que fem és una rotació cap a l'esquerra de 8 bits. Originalment teníem un 1. El 1 passa cap a l'esquerra:

00000001
00000010
00000100
...

JR: Relative jumps to the address. This means that it can only jump between 128 bytes ahead or behind. Can be conditional or unconditional. JR takes up one less byte than JP, but is also slower. Weigh the needs of the code at the time before choosing one over the other (speed vs. size). Per tant, tornem al loop (quan té un sol argument és un jump sense condició)

La idea doncs del codi és que anem fent una rotació cap a l'esquerra d'un bit en el registre A, i el contingut de A el bolquem al port (255), que està associat al display dels LEDs. En conclusió, hem de veure com roten els LEDs.

exemple 2: Un exercici una mica més interessant

org $0
        ld a, 1
left:   out (255), a
        rlca
        cp 128
        jr z, right
        jr left
right:  out (255), a
        rrca
        cp 1
        jr z, left
        jr right

CP is a subtraction from A that doesn't update A, only the flags it would have set/reset if it really was subtracted. Per tant, en fer la substracció és possible que el flag Z (el bit de Zero en el registre F) estigui a 0 o a 1. jr z, right: jr és el jump, i quan té dos arguments, el primer argument és la condició. Mirem el flag Z, si és 1, anem a right.

La idea del codi doncs és que el led que s'encén es desplaça cap a l'esquerra o cap a la dreta. Per decidir el canvi de direcció hem de fer una resta, i mirar el flag Z.

Pas a pas el funcionament és el següent.

ld a, 1 -> A: 00000001
left:   out (255), a -> mostrem el LED: 0000000X
rlca -> A: 00000010
cp 128:
10000000 - 00000010 = (128-2) = 126 = 0x7E = 01111110 (però és una resta sense portar-ne, no afecta al flag Z)
jr z, right -> com que Z=0, no es fa el jump de right, sinó fem el jump de left, per tant, continuem en el mateix bucle.

...
rlca -> A: 01000000
cp 128:
10000000 - 01000000 = (128-64) = 64 = 01000000 (però és una resta sense portar-ne, no afecta al flag Z)
jr z, right -> com que Z=0, no es fa el jump de right, sinó fem el jump de left, per tant, continuem en el mateix bucle.

rlca -> A: 10000000 = (128-128) = 0 = 00000000 (però ara sí que tenim el flag Z=1)
jr z, right -> com que Z=1, ara sí que es fa el jump de right, i per tant saltem a l'altre bucle.

right:  out (255), a -> mostrem el LED: X0000000
rrca -> A: 01000000
cp 1:
01000000 - 00000001 = (64-1) = 63 = 0x3F = 00111111 (però és una resta sense portar-ne, no afecta al flag Z)
jr z, left -> com que Z=0, no es fa el jump de left, sinó fem el jump de right, per tant, continuem en el mateix bucle (estem en el bucle de right).
...
rrca -> A: 00000010
cp 1:
00000010 - 00000001 = (2-1) = 1 = 0x01 (però és una resta sense portar-ne, no afecta al flag Z)
jr z, left -> com que Z=0, no es fa el jump de left, sinó fem el jump de right, per tant, continuem en el mateix bucle (estem en el bucle de right).

rrca -> A: 00000001
cp 1:
00000001 - 00000001 = (1-1) = 0 = 0x01 (però ara sí que tenim el flag Z=1)
jr z, left -> com que Z=1, ara sí que fa el jump de left i saltem a l'altre bucle, i tornem a començar.

Codi màquina. Opcodes del Z80

Tenim els dos programes en ensamblador. S'ha d'obtenir el codi màquina (els bytes que ficarem dins de la EEPROM), i veure com els bytes generats es corresponen amb els opcodes del Z80. És a dir, les instruccions estan associades a unes direccions de memòria que el Z80 entén.

Compilem els dos programes d'exemple que tenim:

$ ./z80asm -I ~/Z80/z80asm-1.8/headers/ -o exemple1.bin -i examples/exemple1.asm
$ ./z80asm -I ~/Z80/z80asm-1.8/headers/ -o exemple2.bin -i examples/exemple2.asm

I els bytes que obtenim són:

$ od -Ax -t x1 exemple1.bin 
000000 3e 01 d3 ff 07 18 fb

$ od -Ax -t x1 exemple2.bin 
000000 3e 01 d3 ff 07 fe 80 28 02 18 f7 d3 ff 0f fe 01
000010 28 f0 18 f7

Repassem els dos exemples:

exemple1.asm:

org $0
        ld a, 1
loop:   out (255), a
        rlca
        jr loop

exemple2.asm:

org $0
        ld a, 1
left:   out (255), a
        rlca
        cp 128
        jr z, right
        jr left
right:  out (255), a
        rrca
        cp 1
        jr z, left
        jr right

I aquí tenim el resum dels opcodes que es fan servir:

3E LD   A,&00     -              SRL  (HL)   SRL  (IY+0)  
D3 OUT  (&00),A   -              SET  2,E    set 2,(iy+0)->e  -
07 RLCA           -              RLC  A      rlc (iy+0)->a    MOS_ARGS
18 JR   &4546     -              RR   B      rr  (iy+0)->b    -
FE CP   &00       -              SET  7,(HL) SET  7,(IY+0)    [z80]
28 JR   Z,&4546   -              SRA  B      sra (iy+0)->b    -
0F RRCA           -              RRC  A      rrc (iy+0)->a    MOS_FF0F

És a dir, cada instrucció té un codi (per ex, LD és 3E), i d'aquesta manera el Z80 sap quina instrucció ha d'executar. A més, donada una instrucció sap si li segueix cap argument, 1 ó 2 arguments.

Torres de Hanoi en Z80 assembler

Hanoi.jpg

Algorisme

L'algorisme de les Torres de Hanoi és un exemple típic de recursivitat:

I en aquest enllaç tenim l'algorisme recursiu en molts llenguatges (sortida orientada a text), entre ells 360 Assembly, però no Z80 assembly:

Algorisme:

Els passos a seguir són:
Step 1 − Move n-1 disks from source to aux
Step 2 − Move nth disk from source to dest
Step 3 − Move n-1 disks from aux to dest

A recursive algorithm for Tower of Hanoi can be driven as follows −

START
Procedure Hanoi(disk, source, dest, aux)

   IF disk == 1, THEN
      move disk from source to dest             
   ELSE
      Hanoi(disk - 1, source, aux, dest)     // Step 1
      move disk from source to dest          // Step 2
      Hanoi(disk - 1, aux, dest, source)     // Step 3
   END IF
   
END Procedure
STOP

I aquí tenim la versió X86 assembly en dues versions, amb push/pop a una pila i, millor', sense push/pop:

Es compara la velocitat amb C++, i resulta ser el doble de ràpid.

Versió x86 Assembly

mov ebx, from
mov ecx, to
mov edx, use
mov eax, howmany
xor esi, esi

call HanoiAsm
mov retcode, esi
jmp EndHanoiAsm
HanoiAsm:
dec eax
jz NextLoop
push eax
xchg ecx, edx
call HanoiAsm
xchg ecx, edx
xchg edx, ebx
pop eax
call HanoiAsm
xchg edx, ebx
NextLoop:
inc esi
ret
EndHanoiAsm:

Assembly, no push/pop:

push ebp
mov ebx, from
mov ecx, to
mov edx, use
xor esi, esi

mov eax, howmany
mov ebp, esp
shl eax, 2
sub ebp, eax
sar eax, 2

call HanoiAsm
pop ebp
mov retcode, esi
jmp EndHanoiAsm
HanoiAsm:
dec eax
jz NextLoop
xchg ecx, edx
call HanoiAsm
mov eax, esp
xchg ecx, edx
sub eax, ebp
xchg edx, ebx
sar eax, 2
call HanoiAsm
xchg edx, ebx
NextLoop:
inc esi
ret
EndHanoiAsm:

Explicació:

mov ebx, from
mov ecx, to
mov edx, use
mov eax, howmany -> eax serà la variable temporal per guardar els discos que falten
xor esi, esi

call HanoiAsm -> fem la primera crida
mov retcode, esi -> movem el contingut de esi a retcode. Què és retcode?
jmp EndHanoiAsm -> sortim del programa

HanoiAsm:
dec eax -> decrementa el contingut de eax en 1 unitat. eax és el howmany
jz NextLoop -> j<condition>. jz <label> (jump when last result was zero). Aquesta és la condició de què quan disk=1 sortim
push eax -> guarda a la pila el valor de eax, que és el valor actual de num de discs per col.locar
xchg ecx, edx -> Exchanges the contents of the destination (first) and source (second) operands
call HanoiAsm -> tornem a cridar: recursivitat
xchg ecx, edx -> tornem a intercanviar
xchg edx, ebx
pop eax -> recuperem de la pila el valor de eax
call HanoiAsm -> tornem a cridar: recursivitat
xchg edx, ebx  -> tornem a intercanviar

NextLoop:
inc esi -> incrementem esi en una unitat i tornem. esi és un registre de propòsit general
ret -> retorna de la crida de HanoiAsm 

EndHanoiAsm: -> sortim del programa

http://www.cs.virginia.edu/~evans/cs216/guides/x86.html

mov ebx, from -> fica el contingut de from a ebx
xor esi, esi -> fica esi=0 quan esi sigui igual a ell mateix

Versió Z80

Per compilar i simular:

joan@joan-portatil:~/projectes/Z80/z80pack-1.34/z80asm$ ./z80asm hanoi_v1.asm
$ cp hanoi_v1.bin ../z80sim/srcsim/

joan@joan-portatil:~/projectes/Z80/z80pack-1.34/z80sim/srcsim$ ./z80sim -z

I la versió amb Z80, finalment! ja he trobat la solució. hanoi_v1.asm:

	ORG 0
numdisks	EQU	3
source	EQU	4
aux	EQU	2
dest	EQU	1

	ld a, numdisks
	ld b, source
	ld c, aux
	ld d, dest
	call HanoiAsm
	ld e,(255)	;mostrem
	jp Fi

HanoiAsm:
	dec a ;decrementem, si a=0 (vol dir que a valia 1), anem a la rutina NextLoop, tornem a deixar a=1, i sortim
	jr z, NextLoop
	push af

	ld h,c
	ld l,d
	ld d,h
	ld c,l

	call HanoiAsm

	pop af
	inc a
	ld e,a	;mostrem
	ld e,b	;mostrem
	ld e,c	;mostrem
	ld e,(0)	;mostrem
	dec a

	ld h,b
	ld l,d
	ld d,h
	ld b,l

	ld h,d
	ld l,c
	ld c,h
	ld d,l

	call HanoiAsm

	ld h,b
	ld l,c
	ld c,h
	ld b,l

	ret

NextLoop:
	inc a
	ld e,a	;mostrem
	ld e,b	;mostrem
	ld e,d	;mostrem
	ld e,(0);mostrem
	ret

Fi:

La solució està fixant-se en el registre E. Surten valors que s'han d'interpretar. Per exemple, per a N=2

E=1 -> moure el primer disc des de la posició 4 (el primer peg, source) fins la posició 2 (2n peg, aux)
E=4
E=2

E=2 -> moure el segon disc des de la posició 4 (el primer peg, source) fins la posició 1 (3r peg, destí)
E=4
E=1

E=1 -> moure el primer disc des de la posició 2 (el segon peg, aux) fins la posició 1 (3r peg, destí)
E=2
E=1

I per què he escollit els valors de 4,2,1 en els registres b,c i d? Doncs perquè visualment podré veure la solució amb els meus 8 leds. El que he de veure en la sortida a mida que vagi fent cicles de rellotge és la solució:

xxxxxxxx -> significa que ve un moviment
.......x
.....x..
......x.


xxxxxxxx -> significa que ve un moviment
......x.
.....x..
.......x


xxxxxxxx -> significa que ve un moviment
......x.
......x.
.......x

hanoi_v2.asm:

	ORG 0
numdisks	EQU	3
source	EQU	4
aux	EQU	2
dest	EQU	1

	ld a, numdisks
	ld b, source
	ld c, aux
	ld d, dest
	call HanoiAsm
	ld e,(255)	;mostrem
	out (255), e
	jp Fi

HanoiAsm:
	dec a ;decrementem, si a=0 (vol dir que a valia 1), anem a la rutina NextLoop, tornem a deixar a=1, i sortim
	jr z, NextLoop
	push af

	ld h,c
	ld l,d
	ld d,h
	ld c,l

	call HanoiAsm

	pop af
	inc a
	ld e,a	;mostrem
	out (255), e
	ld e,b	;mostrem
	out (255), e
	ld e,c	;mostrem
	out (255), e
	ld e,(0)	;mostrem
	out (255), e
	dec a

	ld h,b
	ld l,d
	ld d,h
	ld b,l

	ld h,d
	ld l,c
	ld c,h
	ld d,l

	call HanoiAsm

	ld h,b
	ld l,c
	ld c,h
	ld b,l

	ret

NextLoop:
	inc a
	ld e,a	;mostrem
	out (255), e
	ld e,b	;mostrem
	out (255), e
	ld e,d	;mostrem
	out (255), e
	ld e,(0)	;mostrem
	out (255), e
	ret

Fi:
$  od -Ax -t x1 hanoi_v2.bin 
000000 ff 00 00 3e 03 06 04 0e 02 16 01 cd 12 00 1e ff
000010 d3 ff c3 4c 00 3d 28 28 f5 61 6a 54 4d cd 12 00
000020 f1 3c 5f d3 ff 58 d3 ff 59 d3 ff 1e 00 d3 ff 3d
000030 60 6a 54 45 62 69 4c 55 cd 12 00 60 69 4c 45 c9
000040 3c 5f d3 ff 58 d3 ff 5a d3 ff 1e 00 d3 ff c9
00004f

creat per Joan Quintana Compte, juny 2017

Eines de l'usuari
Espais de noms
Variants
Accions
Navegació
IES Jaume Balmes
Màquines recreatives
CNC
Informàtica musical
joanillo.org Planet
Eines