''***************************************
''*  z8-Emulator  Objekt I/O & Interrupt*
''*  Author: Uwe Nickel                 *
''*  Copyright (c) 2012 by UN-Soft      *
''*  See end of file for terms of use.  *
''***************************************

'Version für nicht doppelt belegte Ausgabepins des Prop!
'==> serielle Ausgabe erfolgt stets bei Beschreiben von Register F0, unabhängig P3M.
'    Wenn in P3M SIO = on gesetzt, erfolgt Interruptanmeldung, sonst nicht.

OBJ
    def           : "tiny_4k_def"    

PUB start (z8reg,z8bus,comptr)

 z8reg_adr := z8reg    'Basis-Adresse Register im HUB-Ram
 z8bus_adr := z8bus
 RxPtr     := comptr

      cognew(@main,z8reg)                          'IO-Cog starten
               

DAT 'Cog für Hardwareemulationen und Interrupt
org 0
'******************************************************************************************************
'---- Mainloop ----------------------------------------------------------------------------------------
'******************************************************************************************************                  
main           '   mov Mem, par
                  mov TxPtr, RxPtr                 'TxPtr aus RxPtr berechnen
                  add TxPtr, #4
                 
'......Portregister 00-03...............................
                  add R0_adr, z8reg_adr
                  add R1_adr, z8reg_adr
                  add R2_adr, z8reg_adr
                  add R3_adr, z8reg_adr
'......Systemregister F0 - FB...........................
                  add SIO_adr, z8reg_adr           'F0
                  add P2M_adr, z8reg_adr           'F6
                  add P3M_adr, z8reg_adr           'F7
                  add P01M_adr, z8reg_adr          'F8
                  add IRQ_adr, z8reg_adr           'FA
                  add IMR_adr, z8reg_adr           'FB
                  add SIOout_adr, z8reg_adr
                  add R54_adr, z8reg_adr
                  add Tcode_adr, z8reg_adr
                  test P2_mask, reset_mask    wz   'teste Bit31 von P2-Maske (neg.Zahl)
                  muxz io_xxx, #$FF                'setze 8Bit
'......Register initialisieren............................................................
reset             mov io_mask, #$00          wz    'Variable 0 setzen, Z-Flag setzen für mux  
                  wrlong io_mask, R0_adr           'R0..3 auf 0 setzen
                  mov temp, #$0B                   'Registeranzahl
                  mov SIO, SIO_adr                 'Startregisteradresse nach Variable
res2              wrbyte io_mask, SIO              'Adresse mit 0 schreiben
                  add SIO, #1                      'nächste Registeradresse
                  djnz temp, #res2                 'zurück, solange Anzahl>0
     '             wrbyte io_xxx, P2M_adr          'Port2 als Eingänge setzen
                  
'......I/O-Pinstatus Port 2/3 ermitteln und ausgeben an Propeller-Pins....................
'......Port 2.............................................................................
                  shl io_xxx, P2_mask              '8 aufeinanderfolgende Bits werden P2- Pinmaske
                  mov io_mask, io_xxx
'......Port 3.............................................................................
                  muxz io_mask, P35_mask           'Ausgabepins setzen, Inputs brauchen
                  muxz io_mask, P36_mask           'nicht festgelegt werden!
                  muxz io_mask, P37_mask 
'.........................................................................................                  
                  mov dira, io_mask                'I/O-Richtung ausgeben

'------------------------------------------------------------------------------------------------                  
go                rdbyte R3, R3_adr
                  rdlong z8bus_val, z8bus_adr      'Bus einlesen
                  test z8bus_val, reset_mask  wz
       if_nz      jmp #reset  
'------------------------------------------------------------------------------------------------

'-----Emulationen wegen Nichtbenutzung ISR der original Bidschirmsteuerung-----------------------                 
'.....Tastaturklick - Emulation..................................................................
keyclick          rdbyte temp, Tcode_adr           'Tastencoderegister 6D einlesen
                  test temp, #%1000_0000      wz
        if_nz     add t1_test, t1_add_val     wc
  if_c_and_nz     xor R3, #$40                     'Bit6 in R3 = P36 negieren
  if_c_and_nz     wrbyte R3, R3_adr

keyclick_end      

'......R54- Emulation (wegen Input-Tastaturroutine ohne T0-ISR)..................................
R54               add t0_test, t0_add_val     wz   'test
      if_nz       jmp #R54_end
                  rdbyte temp, R54_adr 
                  sub temp, #1             
'......per direktem Beschreiben R54 für TINY.....................................................
                  wrbyte temp, R54_adr             'schreiben
R54_end

'-----P2/3 - Emulation---------------------------------------------------------------------------           
'-----I/O- Richtungen für Propellerpins entspr. P2-Richtung setzen-------------------------------
                  cmp io_xxx, #0        wz
       if_z       jmp #P2dir_end           
                  rdbyte P2M, P2M_adr              'Port2 I/O-Richtungsregister Z8 lesen  (Bit=1 -> Input)
                  mov io_mask, dira                'IO-Richtungsregister Prop einlesen
       
'.....P2-Richtung...........................................................................
                  andn io_mask, io_xxx             'alte P2-Werte ausblenden      
                  mov temp, P2M                    'nach Arbeitsvariable und
                  xor temp, #$FF                   'bitweise negieren
                  shl temp, P2_mask                'verschieben um Portpinnummer 
                  or io_mask, temp                 'in IO-Richtungsregister Prop einfügen 
                  mov dira, io_mask                'I/O-Richtung für alle Pins ausgeben
P2dir_end 
'.....P3-Richtung...........................................................................                   
'     - muss nicht festgelegt werden, da hardwareseitig fest P30..33 Input, P34..37 Output
 

'-----P3 - Emulation------------------------------------------------------------------------------
                  rdbyte P3M, P3M_adr         
'Nutzung P3M bei TINY und Emulation................................................
'Bit D0    - Port2 Ausgänge:
'            - nicht benutzt bei Emulation, da Prop-Hardware kein open drain ermöglicht
'Bit D1    - unused
'Bit D2    - Betriebsart P32 und P35
'            - immer 0 (Ein/Ausgänge), kann wegen Nutzung P0 als Teil des externen Speicherbusses
'              ohnehin nicht sinnvoll als Handshake für P0 dienen
'Bit D3,D4 - Betriebsart P33 und P34
'             - in Emulation nur Kombination 00 sinnvoll,
'               weil weder Handshake P1, noch /DM-Signalausgabe an P34 für Emulation vernünftig
'Bit D5    - Betriebsart P31 und P36
'             - 0: I/O- Betriebsart, 1: Handshake Port2 
'             - 
'Bit D6    - Betriebsart P30 und P37
'             - 0: I/O- Betriebsart, 1: SIO- I/O 
'             - I/O- Pins für serielle Daten sind in der Emulation gleichzeitig die Portpins der seriellen
'               Programmierschnittstelle des Prop (P30 und P31).
'Bit D7    - Betriebsart der SIO
'             - 0: keine Parität, 1: mit Parität
'             - im Emulator: Schnittstelle mit/ohne Interruptauslösung

     '          
'Die an den Eingabe Portpins (P30..P33) anliegenden Pegel werden unabhängig der Programmierung von P3M immer in R3
'eingeblendet.
'Wenn Output- Portpin mit Spezialfunktion in P3M belegt, dann hat direktes Beschreiben von R3 in 
'der entsprechenden Bitposition keine Wirkung auf die zugeordneten Portpins beim Original-Z8.
'Im Emulator kann R3 immer beschrieben werden, Ist keine Spezialfunktion für die Portleitung festgelegt, so
'werden die Signale der entsprechenden Hardware (Timer..) nicht in R3 übernommen! 
     
                  mov IRQ_test, R3                 'alte R3-Bitbelegung nach Testvariable
'.....Input-Linien P30..33.....................................................................
                  test ina, P30_mask          wz   'neue Werte von Pins einlesen
                  muxz R3, #%0000_0001
                  test ina, P31_mask          wz
                  muxz R3, #%0000_0010
                  test ina, P32_mask          wz
                  muxz R3, #%0000_0100
                 'Emulation P33 erfolgt nicht, s. oben!

'.....Output-Linien P34..37....................................................................
                       
'.....P34 - Ausgabeemulation...................................................................
                 'Emulation erfolgt nicht, s. oben!!
'.....P35 - Ausgabeemulation...................................................................
                  test R3, #$20               wz   'Bitbelegung               
                  muxnz outa, P35_mask             'ausgeben, kein Betriebsarttest,da
                                                   'Handshake Port1 nicht sinnvoll!
'.....P36 - Ausgabeemulation...................................................................
                  test P3M, #%0010_0000       wc   'Handshake Port2/IO-Betriebsart?
                  test R3, #$40               wz   'Bitbelegung
       if_nc      muxnz outa, P36_mask             'wenn IO-Betriebsart, dann Bit ausgeben
                  
'.....P37 - Ausgabeemulation...................................................................
                  test R3, #$80               wz   'Bitbelegung
                  muxnz outa, P37_mask             'ausgeben, kein Betriebsarttest, da
                                                   'SIO-Out separat über serielle Schnittstelle des Prop
'-----Interruptgebende Emulationen------------------------------------------------------------------
                  xor IRQ_test, R3                 'P3- Interrupts der neugelesenen Werte
                  andn IRQ_test, R3                'in "Testvariable" einfügen
                  and IRQ_test, #%0000_0111        'und maskieren


'-----P2 - Emulation-----------------------------------------------------------------------------
                  cmp io_xxx, #0        wz
        if_z      jmp #P2_end                              
'.....P2 - I/O - Werte lesen/schreiben........................................................
                  mov Pin, ina                     'Werte aller Pins einlesen
                  rdbyte R2, R2_adr                'Wert aus R02 einlesen
                  or P2M, P2_xxx         
                  test P3M, #%0010_0000       wc   'Handshake/IO-Betriebsart?
        if_c      jmp #P2_hs                        'Sprung, wenn ja
          
P2_io             ror Pin, P2_mask                 'P2_Wert auf Bitposition 0..7 verschieben
                  and Pin, P2M                     'Werte derOutputpins P2 maskieren (0 setzen)
                 
                  andn R2, P2M                     'alte Input-Werte ausblenden (0 setzen)
                  or R2, Pin                       'gelesene Input-Bits dazu und
                  wrbyte R2, R2_adr                'nach R02 zurückschreiben   
                  rol R2, P2_mask                  'Wert verschieben um Portpinnummer
                  mov outa, R2
                  jmp #P2_end

'.....P2 mit Handshake an P31/36...................................................................
P2_hs             test z8bus_val, R2wr_mask   wz   'neuer R2_Ausgabewert?
        if_z      jmp #P2_hs_wr                    'Sprung, wenn ja

'.....lesen........................................................................................        
P2_hs_rd          muxnz outa, P36_mask             'RDY aktiv setzen (H)
                  test IRQ_test, #%0000_0100  wz   'IRQ2 = H/L_Flanke an P31? entspr. /DAV
        if_z      jmp #P2_end                      'Sprung, wenn nicht, sonst
                  ror Pin, P2_mask                 'P2_Wert auf Bitposition 0..7 verschieben
                  andn R2, P2M                     'alte Input-Werte ausblenden (0 setzen)
                  jmp #P2_end

'.....schreiben....................................................................................                  
P2_hs_wr          test Pin, P31_mask          wz   'RDY des Empfängers aktiv (H) ?
        if_z      jmp #P2_end                      'Sprung, wenn nicht, sonst
                  ror Pin, P2_mask                 'P2_Wert auf Bitposition 0..7 verschieben
                  and Pin, P2M                     'alte Werte für Outputpins 0 setzen
                  or Pin, R2                       'neue Werte setzen
                  rol Pin, P2_mask                 'an Pinposition zurückschieben
                  mov outa, Pin
                  andn z8bus_val, R2wr_mask        'Flag rücksetzen    
                  muxz outa, P36_mask              '/DAV aktiv setzen (L)

P2_end  

'..........................................................................................

{{----serielle I/O - Emulation---------------------------------------------------------------------
  In Register F0 - SIO steht zu schreibendes/lesendes Byte und geht/kommt nach/aus Puffer
  des seriellen Treibers
  Wenn in F7 - P3M, Bit6 (SIO=on) gesetzt ist, werden Interrupts ausgelöst, es gilt:
  In Register FA - IRQ wird IRQ4 (Bit4) Status für serielle Ausgabe gesetzt: 
                                1- wenn Byte aus F0 übergeben
                                0- wenn nicht
                            IRQ3 (Bit3) Status für seriellen Empfang gesetzt:
                                1- wenn neues Byte in F0 vorhanden    
                                0- wenn nicht       
}}
'......schreiben - Byte ausgeben..........................................................
SIOout            test z8bus_val, SIOwr_mask  wz   'Status testen(wird gesetzt bei Schreiben auf Register F0), 
         if_z     jmp #SIOout_end                  'wenn kein neuer Wert springen
                  rdlong  temp,TxPtr               'Bereitschaft Schreibpuffer testen                                   
                  shl temp,#1                 wc   'C=1, wenn bereit
         if_nc    jmp #SIOout_end                  'wenn keine Bereitschaft springen
                  rdbyte SIO, SIOout_adr           'sonst Registerwert holen
                  wrlong SIO,TxPtr                 'nach Schreibpuffer schreiben
                  andn z8bus_val, SIOwr_mask       'Status rücksetzen
                  wrlong z8bus_val, z8bus_adr      'nach Bus schreiben
                  test P3M, #%0100_0000       wz   'SIO = on?   
                  muxnz IRQ_test, #%0001_0000      'wenn SIO, dann IRQ4 setzen
SIOout_end
                   
'......lesen - Byte einlesen..............................................................                     
SIOin             test z8bus_val, SIOrd_mask  wz   'Status testen(wird rückgesetzt bei Lesen von Register F0), 
         if_nz    jmp #SIOin_end                   'wenn nicht abgeholt, dann Sprung
                  rdlong  temp,RxPtr               'Bereitschaft Empfangspuffer testen                                   
                  shl temp,#1                 wc   'C=1, wenn kein Byte verfügbar       
         if_c     jmp #SIOin_end                   'wenn kein Byte, dann springen
                  shr temp, #1
                  wrlong MinusOne, RxPtr           'Mark the byte as having been read 
                  wrbyte temp, SIO_adr
                  or z8bus_val, SIOwr_mask         'Status setzen
                  wrlong z8bus_val, z8bus_adr      'nach Bus schreiben
                  test P3M, #%0100_0000       wz   'SIO = on?   
                  muxnz IRQ_test, #%0000_1000      'wenn SIO, dann IRQ4 setzen 
SIOin_end
'........................................................................................
                  or IRQ_test, IRQ_test       wz   'Interruptanforderung SIO, P3?
                  rdbyte IRQ, IRQ_adr              'einlesen Interrupt-Status    
         if_z     jmp #IO_end                      'Sprung, wenn nicht, sonst
                  or IRQ, IRQ_test                 'neue Interrupts einblenden und
                  wrbyte IRQ, IRQ_adr              'IRQ Anforderungen schreiben                  

{{-----------------------------------------------------------------------------------------------
'......TIMER-Interrupt - Emulation........................................................
'      in separatem Cog für T0/T1
'-----------------------------------------------------------------------------------------------_}}
IO_end
                 
{{-----------------------------------------------------------------------------------------------
'------Interruptverarbeitung--------------------------------------------------------------------
'-----------------------------------------------------------------------------------------------}}
'                  rdbyte IRQ, IRQ_adr              'einlesen Interrupt-Status     
                  test z8bus_val, istop_mask  wz   'genereller Interruptstop durch Tastatur?
         if_nz    jmp #int_end                     'Sprung, wenn ja, sonst
                  rdbyte IMR, IMR_adr              'IMR einlesen
                  test IMR, #%1000_0000       wz   'Interrupts global freigegeben?
         if_z     jmp #int_end                     'Sprung zurück,wenn nicht, sonst
                  mov IRQ_test, IRQ
'-----------------------------------------------------------------------------------------------
'......Wegen Emulation mit/ohne BWS-ISR.........................................................
                  test z8bus_val, t0irq_mask  wz   'T0- Interruptstop durch Tastatur? 
         if_nz    andn IRQ_test, #%0001_0000       'T0/SIOout-Interrupt ausblenden !!!!!!!!!!!!!
'-----------------------------------------------------------------------------------------------
                  and IRQ_test, IMR           wz   'gesperrte Interruptanmeldungen ausblenden u. testen
         if_z     jmp #int_end                     'Sprung zurück,wenn keine gültigen Anforderungen, sonst
                  andn IMR, #%1000_0000            'globale Interruptsperre  (bit7=0)
                  wrbyte IMR, IMR_adr              'setzen
                  
                  mov PC, PC_int                   'Pointer auf Interrupttabellenadresse IRQ 5  
                  mov temp, #%0010_0000            'Maske auf IRQ5
:ma1              test IRQ_test, temp         wz   'testen IRQ mit Maske
         if_nz    jmp #:ma2                        'wenn IRQ angemeldet und freigegeben, dann Sprung
                  sub PC, #3                       'sonst Tabellenadresse - 3
                  shr temp, #1                wz   'Maske auf nächst niedrigen Interrupt
         if_z     jmp #int_end                     'Sprung, wenn Maske = 0 
                  jmp #:ma1                        'zurück, solange nicht 0
                  
:ma2              andn IRQ, temp                   'gefundenen IRQ rücksetzen
                  wrbyte IRQ, IRQ_adr              'und zurückschreiben
                  andn z8bus_val, adrdat_mask      'alte Werte ausblenden
                  or z8bus_val, INT_mask           'IRQ-FLAG dazu
                  or z8bus_val, PC                 'ISR-Adresse dazu
                  wrlong z8bus_val, z8bus_adr      'nach Bus schreiben              
int_end  
     
'-----All End --------------------------------------------------------------------------------------------
                  jmp #go

{{*********************************************************************************************************
'---- Data & Variable ------------------------------------------------------------------------------------
'*********************************************************************************************************}}                    
z8reg_adr     long   0
z8bus_adr     long   0
P2_mask       long   def#P2startpin
P30_mask      long   def#P30     
P31_mask      long   def#P31     
P32_mask      long   def#P32     
'P33_mask      long   def#P33     
'P34_mask      long   def#P34     
P35_mask      long   def#P35     
P36_mask      long   def#P36     
P37_mask      long   def#P37      

RxPtr         long   0
TxPtr         long   0   
'io_xxx        long   $FF
P2_xxx        long   $FF_FF_FF_00 
MinusOne      long    -1
reset_mask    long   %10000000_00000000_00000000_00000000 
INT_mask      long   %00100000_00000000_00000000_00000000
SIOrd_mask    long   %00010000_00000000_00000000_00000000
SIOwr_mask    long   %00001000_00000000_00000000_00000000
t0irq_mask    long   %00000010_00000000_00000000_00000000           
istop_mask    long   %00000001_00000000_00000000_00000000
R2wr_mask     long   %00000000_10000000_00000000_00000000   
adrdat_mask   long   %00000000_11111111_11111111_11111111    
SIO_adr       long   def#SIO
P01M_adr      long   def#P01M
P2M_adr       long   def#P2M
P3M_adr       long   def#P3M
IRQ_adr       long   def#IRQ
IMR_adr       long   def#IMR
R0_adr        long   def#R0
R1_adr        long   def#R1
R2_adr        long   def#R2
R3_adr        long   def#R3
SIOout_adr    long   def#SIOout
R54_adr       long   $54
Tcode_adr     long   $6D 
PC_int        long   $0000_080F
t1_test       long   $0000_0000
t1_add_val    long   $0040_0000
t0_test       long   $0000_0000
t0_add_val    long   $0800_0000
                                    
'Mem            res   1


io_mask        res   1
io_xxx         res   1
z8bus_val      res   1      
temp           res   1
z8reg_ptr      res   1
Tstatus        res   1
P2M            res   1
Pin            res   1
P3M            res   1
R3             res   1
R2             res   1  
IRQ            res   1
SIO            res   1
IMR            res   1
IRQ_test       res   1
PC             res   1
 