Wednesday, August 30, 2017

Super Serial Makes a Personal Discovery

   This seems like the next possible task for such a device. I've kept a yellow post-it note under the monitor with a listing of what card resides in which slot and have often lost it. Having to then remove the monitor to retake inventory of the system.  This seemed like a good time for the next evolution of code, definitely not as complicated as the IRQ issue, but requiring some thought, none the less.

     I have a copy of Apples SSC manual, both versions of the manual which i refer to as the crappy consumer grade and the deluxe engineering version.  They both discuss card identification by way of four bytes that will appear in predictable locations of memory. This further matches up with the Apple IIe technical reference manual on page 278; in short....
                             $Cs05 = 38
                             $Cs07 = 18
                             $Cs0B = 01
                             $Cs0C = 31           Where s = the slot number

     I figured there must be code out there already, but I wanted to take a crack at this without influence. So I though to myself all I need is a process which starts at slot 1 and tries to read 38 from $C105 if it does, then it tries to read 18 from $C107. If that works try to read 01 from $C10B and finally 31 from $C10C.  Should any of the expected values not come back during this test at any time go to the next slot and start this test all over, till you run out of slots, then give up.  If you do make it through all four numbers, well then, you've found your SSC card!

     As I started thinking about this I realized that there are 7 slots to test with four cycles each, that's a large volume of assembly code for slot detection. Actually 28 possible iterations.  After getting one full cycle working in this longhand method I constructed a more efficient design.  Quite clever actually.

     In the INIT portion the value SLOT is set to 01 and the MSB is set to C1 for the first slot.  The MSB need only be incremented along with the slot value to move to the next slot to test. So the next increment would have the SLOT at 02 and the MSB at C2. and so on until 07 and C7 as the last one.

At the bottom of the program is the storage of 8 bytes of data, its relevance is shown below

INDEX(X) 0     1     2     3

SSCID  38 18 01 31    Expected return data from memory address 
LSBVAL 05 07 0B 0C    Least significant byte of that address

Now we see that labels LSB and MSB sit next to each other in the zeropage.

LSB        = $08         ; Bottom half of memory address
MSB        = $09         ; Top half of memory address

So with a small routine using the same X as an index to pull from SCCID as well as LSBVAL I can get both the answer im looking for as well as the other half of the memory address I need to pull that answer.

MSB     =   C1    already stored in $09 from INIT
LSB     =   05    Pulled from LSBVAL index 0, stored in $08
SSCID   =   38    Expected answer SSCID index 0
      LDY #$00 ; no offset
      LDA (LSB),Y    =  LDA (C1&05),0   =   LDA $C105

The value coming back into a is stored in TESTBYTE so that SSCID loaded into a can be compared against TESTBYTE to see if they are the same value.  If they are, in this case 38,  ONLY X IS INCREMENTED and the cycle is repeated, this time...

MSB     =   C1    already stored in $09
LSB     =   07    Pulled from LSBVAL index 1, stored in $08
SSCID   =   18    Expected answer SSCID index 1
      LDY #$00 ; no offset
      LDA (LSB),Y    =  LDA (C1&07),0   =   LDA $C107

     Again, if X should make it to the 4th interval, then the card is found and the program will jump out to FOUND.  If there is no match X will reset back to zero and the MSB and SLOT will be incremented, this process starts all over.

     If the card is found, more code below displays the slot number to the screen and a simple indication that it has been found.  also, 4 ROL multiply the slot number by 0x10 (dec16) and store it back into slot. All of the previously mentioned connections into to the ACIA are listed here without any offset onto of the program as shown

DATAREG     = $C088       ; Needs slot added when called
STATUSREG   = $C089       ; Needs slot added when called
COMMANDREG  = $C08A       ; Needs slot added when called
CONTROLREG  = $C08B       ; Needs slot added when called

 To access them in a program, The offset in SLOT would have to be loaded into Y and then used as an offset in the call.  Considering sending a byte to the ACIA as data:

            LDY SLOT                              
            STA DATAREG,Y  

     So was all of this worth it from a memory perspective?  In consideration that almost half of the example program below is just printing some sort of output to the screen in the spirit of view-ability the total number of bytes ACTUALLY NEEDED to run this program, and assign the SLOT and move onto the next item is just 77 Bytes.  By comparison, simply storing the text "Please specify the slot of your SSC card: " to memory takes up 42 bytes all by itself, the code to print it to the screen takes at least 12 bytes, now were at 54 bytes,  then the KEYIN, strobe, associated validation checking of the human input, math to do all of the same conversion and assignment to the SLOT, testing and deployment. Also, what if the wrong number was given, is there exception handling? No doubt it would be way over 77 bytes for a program that has a manual entry no auto discovery and the propensity for human error.

     Its worth noting that in either case, if such a program were being constructed that every last byte had to be accounted for, there is no reason why all of this detection code couldn't simply be copied over by the main program after the fact, having stored the correct location in SLOT, none of the detection code has any further practical use in memory once the task is completed.  The last action in FOUND could tell PRODOS to load and execute a MAIN.bin application who's ORG = $2000 effectively copying directly over all of this on load.

Click here for PRODOS DSK file including Merlin source and executable

*         **** COMMODORE 64 BASIC V2 ****          *
*      64K RAM SYSTEM  38911 BASIC BYTES FREE      *
*     READY.                                       *
*     LOAD"SSCFINDER",8,1                          *
*      ---Auto detecting SSC card code---          *
*  Written and compiled natively on Merlin 8       *
*  No Emulators used...... TASM style formatting   *
*  Uses 4 known bytes of data that the SSC will    *
*  return if the offset for the memory address     *
*  matches the slot the SSC is in. Those SSCID's   *
*  & corresponding LSB's are loaded together in    *
*  the same loop iteration to keep the code very   *
*  small, only the MSB increments to change the    *
*  card. LSB and MSB are used as (LSB),msb to call *
*  the individual addresses to read.               *
*  Once found SLOT is multiplied by 0x10 and then  *
*  Re-stored in SLOT for use as an offset value    *
*  for DATAREG, COMMAND and CONTROL                *
*            LDY SLOT                              *
*            STA DATAREG,Y                         *
*  Free for all to use - Jordan Rubin 2017         *
*                  *
*  More at       *
          ORG $2000       ; HIRES area unused and safe
COUT        = $FDED       ; Like C64 $FFD2
PRBYTE      = $FDDA       ; HEXPRINT
TESTBYTE    = $0A         ; test byte area
LSB         = $08         ; Bottom half of memory address
MSB         = $09         ; Top half of memory address
HOME        = $FC58       ; CLS & Cursor to top POS
SLOT        = $07         ; Testing and storage of slot
DATAREG     = $C088       ; Needs slot added when called
STATUSREG   = $C089       ; Needs slot added when called
COMMANDREG  = $C08A       ; Needs slot added when called
CONTROLREG  = $C08B       ; Needs slot added when called

************** Start of program
          JSR HOME       ; Clear screen, and top left start
          LDY #$00

************** Prints Message on top of screen
          JSR COUT
          CPY #$19

************** Init routine start at slot one and MSB C1
          LDA #$01       ; Starting slot is 1
          STA SLOT       ; Store it
          LDA #$C1       ; Starting MSB is C1
          STA MSB        ; Store it
          LDX #$00       ; Init X

************** The main testing routine
          LDA LSBVAL,X   ; LSB comes from data at at LSBVAL
          STA LSB        ; For each x - 05, 07, 0B, 0C
          LDY #$00       ; We dont want an offset for this action
          LDA (LSB),Y    ; Cs05 content load, indirect index
          STA TESTBYTE   ; Store return to temp memory area
          LDA SSCID,X    ; Load expected SSCID value for x to a
          CMP TESTBYTE   ; Compare recieved value to a
          BNE NEXTSLOT   ; Not equal? Not an SSC, try next slot
          INX            ; Equal? X++
          CPX #$03       ; All four bytes passed?
          BEQ FOUND      ; We have found the SSC!!!
          JSR ROUTINE    ; Not four yet, repeat routine....

************** Action to proceed to next slot to test
          LDX SLOT       ; Load current slot into X
          INX            ; Increment it
          CPX #$07       ; Passed the last slot to test
          BEQ GIVEUP     ; Give up, no SSC found....
          STX SLOT       ; Store new current slot value
          LDX MSB        ; load current MSB into X
          INX            ; Increment it
          STX MSB        ; Store it
          LDX #$00       ; Init X
          JSR ROUTINE    ; Try Routine again

************** What to do when no SSC is found
          LDX #$00       ; Init X
          LDA NOTFOUND,X ; Print not found Label
          JSR COUT
          CPX #$0D
          BNE :PR
          JMP $3D0       ; Back to Basic

************** When an SSC is found
          LDX #$00       ; Init X
          LDA FOUNDLABEL,X ; Print found label
          JSR COUT
          CPX #$0D
          BNE :PR
          LDA SLOT
          JSR PRBYTE     ; Print slot num to screen
          LDA SLOT       ; Load SLOT into A
          ROL            ; Multiply by 0x10
          STA SLOT       ; Store slot into A, official slot value!
          JMP $3D0       ; Back to Basic

MESSAGELABEL ASC "Checking for SSC Card... "
FOUNDLABEL   ASC "Found in --> "
NOTFOUND     ASC "Was not found"
SSCID        HEX 38,18,01,31 ; 4 identifying bytes for SSC
LSBVAL       HEX 05,07,0B,0C ; LSB for identifying memory areas

Wednesday, August 23, 2017

Super Simple Super Serial Sample Source

     I have struggled to find the most basic of information about Apple II related machine coding information. Its not as freely available as people would like to have themselves believe it is. In searching, having often found the same struggles of others in the past, some who had given up years ago for lack of understanding or availability of information. While that is subjective, there is as it seems, a complete black hole with regard to several facets of the platform, much having to do with complications dealing with rom revs and later model incompatibilities compounded by different versions of software one might be running at the time and peripherals that might be attached. Even with information in front of you, its wrong, without context, that, being fragmented bits found in old newsgroups from Compuserve, leaves you worse for wear. Being a pampered Commodore guy, I am unencumbered by these issues, I don't deal with operating systems, I keep a computer in each and every disk drive, have a large support group and interface with the processor in its purest form except when workarounds for terrible implementations are required.

     Getting back to the Apple II, it would seem that one of the biggest offenders is the handling of interrupts.   Documentation of the past shows that the initial deployment didn't go very well at all. improvements were made, but subsequent OS control of interrupts further complicated matters.  Ive recently picked up a copy of Assembly lines, a wonderful reprint, thanks Chris, which i've referred to extensively in this project to translate the spoken Commodore into Applese, interesting point though, there is no mention of IRQ's or NMI's in that book. Probably for the aforementioned reasons. Given the book is a reprint, and IRQ kinda rolled out DOA, well..... anyway, to that end, I have slowly worked through old documentation, notes, more PRODOS crap than i'd ever care to know about and lots of debugging to provide a standard setup to build upon. Basically what I have at home, and use this going forward.

     The System is an Apple //e enhanced with an 80 column card providing 128K. The 80 column card and the extra 64k is of no consequence to this program. By default, I've included but commented out 80 column mode in the source code.  Im using Merlin 8 version 2.58 and added BASIC.SYSTEM to disk 2 so that BRUN could be employed for quicker development.  No emulation was used in this endeavor.  Final disks were pushed with ADTpro to a linux box, the source extracted for viewing here with Applecommander.

     The program is minimalist by design. Made to do as little as possible to meet the requirements set forth.  A connection is made to another computer via crossover cable running Minicom, Gtkterm or an equivalent.  The program starts with a blank screen.  Each key pressed on the Apple appears on both the apple and the distant end computer.  With the Apple, starting at the top left and advancing with each character.  When the distant end presses a key in will appear on the bottom row of the Apple starting at the bottom left and advancing to the right until the 15th character at which point it will start updating the bottom left again. A quasi visible ring buffer.  If you don't see a feature in this program its because it was not necessary.  SSC location detection, enhanced apple detection, split screen vector graphics support.... beyond the scope of this exercise.  WORKING SEND RECEIVE SSC ASSEMBLY LANGUAGE PROGRAM WITH PRODOS IRQ SUPPORT. THATS IT.

     The actual buffer that is offloaded from the SSC ACIA is in the zero page at $09.  The offloading portion is done during the IRQ.  That IRQ signal gets kicked off because of the SSC card receiving a byte, it doesn't mean the computer knows this program owns it.  Because the status register indicates such activity on the SSC itself, it can be tested in the IRQ code for ownership, acted upon or dismissed.   Nothing special about that, run of the mill ACIA stuff.  The fact that the IRQ code drops it in the BUFFER allows for the READBUFFER routine in the main program to test that zero page byte for new data and act upon it, keeping the heavy lifting out of the IRQ.

     So START clears the screen and initialized the SSC.  It also conducts some PRODOS voodoo to onboard the IRQ with its own identifier into one of 4 IRQ placeholders into PRODOS.  Ill admit i'm not a fan, this is not for the purist at heart.  But if you want mouse and disk drive support with your IRQ, well then....

     MAIN JSR'S to READBUFFER to see if its not #$00 to act upon it. If its #$00, wait for a keyboard event by jumping back to MAIN and getting stuck in this loop forever.

     There are only 2 ways out of this loop.   Press a key on the keyboard is the first.  If you press it it will appear on the screen, have the 7th bit removed from the byte and then a JSR to the SEND routine where it will check the STATUSREGISTER until it is ready to accept the byte,  once ready will send it off to the ACIA touch STROBE to clear that KEYSTROKE and loop back to MAIN

     The other way is for the IRQ to have left something in the BUFFER, that would make it no longer #$00.  If that is the case READBUFFER will pull that value out of the BUFFER and print it to $0750, AKA BOTTOM LEFT, offset by the COUNTER.   Once that value is printer the BUFFER is set back to #$00 and the COUNTER is incremented.  Should the counter reach #$15, the COUNTER will be set back to 0, completing the loop.  In either case , reset counter or not, the program will loop back to main once the receive byte is printed.

     I could further reduce the size of this program at the expense of readability and decide that it violated the goal of this exercise so I chose not to.  The parameters of which have already been stated.  So if anyone wants to provide a means for a new feature or make this smaller I have no interest whatsoever.  Should you find an outright bug of course, thats a different matter.  Furthermore, If anyone would like to provide some context with regard to the PRODOS IRQ onboarding voodoo as well as the bottom area labeled IRQLIST, that would be most welcomed.

     So,  did I need the IRQ.  Probably not.  I probably could have polled the STATUSREG if the timing was acceptable and program flow allowed up to a given speed.  Mileage varies.  At some point in a far extreme a case is to be made for NMI's which is a whole other business.  Another case is looking at the benefit of constant polling for data versus the SSC informing of an event and acting upon it external from the program.

DSK file is included below in google drive link as well as highlighted source.

Thank you and enjoy,


Click here for PRODOS DSK file including Merlin source and executable

*         **** COMMODORE 64 BASIC V2 ****          *
*      64K RAM SYSTEM  38911 BASIC BYTES FREE      *
*     READY.                                       *
*     LOAD"SSCTEST",8,1                            *
*  Super Duper Simple SSC IRQ TEST                 *
*  Written and compiled natively on Merlin 8       *
*  No Emulators used...... TASM style formatting   *
*  Requires Apple IIe, most likely enhanced with   *
*  working interrupts and this code must also be   *
*  run from PRODOS to work...                      *
*  execute with --->          BRUN SSCTEST         *
*  Do not execute from the monitor                 *
*  Change SLOT value as necessary , set for 2      *
*  Default values are for N-8-1 9600 baud conn.    *
*  Transmit appears on top, RX buffer is a 15 byte *
*  rotating buffer within text memory on bottom of *
*  the screen.                                     *
*  Free for all to use - Jordan Rubin 2017         *
*                  *
*  More at       *
          ORG $2000      ; HIRES area unused and safe
COUNTER    = $08         ; Rotating counter for RXbyte
BUFFER     = $09         ; Where IRQ will store RXbyte
SLOT       = $20         ; This is slot 2 where SSC is
DATAREG     = $C088+SLOT
HOME       = $FC58       ; CLS & Cursor to top POS
COUT       = $FDED       ; Like C64 $FFD2
KYBD       = $C000       ; Keyboard map point
STROBE     = $C010       ; Keyboard strobe map point
MLI        = $BF00       ; PRODOS MLI entry point

************** Start of program
                         ; JSR $C300 ; Uncomment for 80 COL
          JSR HOME       ; Clear screen
          JSR INIT       ; Run init for SSC Card

************** Main loop of program
          JSR READBUFFER ; Read from where IRQ stores incoming
          LDA KYBD       ; Poll keyboard pointer
          CMP #$80       ; Check keyboard for activity
          BCC MAIN       ; Keep reading buffer till TXkey event
          JSR COUT       ; print TX locally on top of screen
          AND #$7F       ; shut off 7th bit for ESC issue
          JSR SEND       ; Send the byte to the ACIA outbound
          STA STROBE     ; Remove event by accessing
          JMP MAIN

          LDA BUFFER     ; Pull Buffer content
          CMP #$00       ; Change since last initialization?
          BNE :PRINT     ; If so, go to PRINT routine
          LDY COUNTER    ; Load COUNTER into Y
          INY            ; Increment it
          STA $0750,Y    ; Store BUFFER content to our screen bottom
          LDA #$00       ;              .......and offset by counter
          STA BUFFER     ; Reset Buffer to Null
          TYA            ; copy y to a
          CMP #$15       ; Are we on the Hex15 rotation
          BEQ RESETCOUNTER ; if so Reset the counter to 0
          STY COUNTER    ; if not store the counter

          LDA #$00
          STA COUNTER    ; COUNTER is 0 again.....

************** INIT Sets up the SSC and IRQ stuff
          LDA #$00
          STA COUNTER    ; set counter to 0
          JSR MLI        ; PRODOS ENTRY
          DB $40         ; MORE PRODOS

* BIT [6-5]  LENGTH - 8 (00) / 7 (01) / 6 (10) / 5 (11)
* BIT [4]    CLK - EXTERNAL (0) / INTERNAL (1)
* BIT [3-0]  BAUD - 9600 (1110) / 19.2K (1111)
*                   4800 (1100) / 2400  (1010)
          LDA #011110

* BIT [7-5] PARITY- (000)DIS  (001)O  (011)E  (101)M (111)S
* BIT [4]   ECHO -  (0) NORM / (1) ON
* BIT [3-2] TXINT- (00)DIS:RTSH (01)EN:RTSL
*                  (10)DIS:RTSL (11)DIS:SBRK
          LDA #001001
          LDA DATAREG    ; Pull from DATAREG on init just because

************** IRQ This is the IRQ code
          CLD            ; Required by PRODOS
          LDA STATUSREG  ; Was it us who caused this interrupt
          BMI :ACTION    ; If so, Action!!!
          SEC            ; If not inform, not us....
          LDA DATAREG    ; Load byte directly from dataregister
          STA BUFFER     ; and drop it in the ZP BUFFER
          CLC            ; clear interrupt

************** SEND Check status and send a byte to ACIA
          LDA STATUSREG  ; Load status register
          AND #$10       ; mask for ready bit
          BEQ :CHECK     ; not ready? keep checking
          STY DATAREG    ; ready?? store byte

************** IRQLIST For PRODOS, overhead requirements
          DB 2,0
          DW IRQ