; SIM8800 ; Altair 8800 simulator by Charles Mangin, 2019 ;8080 emulator code: ; APPLE-80 - an 8080 simulator-debug package Copyright (c) 1979 by Dann Mccreary DSK SIM8800 ORG $9000 ; ---------------------------------------------------------------------------- ; TODO: ; optimize for speed, 65C02 - target is Enhanced IIe, 128K RAM ; port to 65816? 24bit addresses, 16 bit registers, 64k contiguous AUX, 2.8mhz ; ; ---------------------------------------------------------------------------- ZERO EQU $00 SPEED EQU $02 ; ??? INTE EQU $03 ; INTERRUPT ENABLE - 00 == interrupts enabled, 01 == no interrupts PSW EQU $04 ; PROCESSOR STATUS SIMA EQU $05 ; ACCUMULATOR SIMM EQU $06 ; DUMMY MEMORY REGISTER SIML EQU $07 ; HL REG PAIR SIMH EQU $08 ; HL REG PAIR SIME EQU $09 ; DE REG PAIR SIMD EQU $0A ; DE REG PAIR SIMC EQU $0B ; BC REG PAIR SIMB EQU $0C ; BC REG PAIR SPL EQU $0D ; STACK POINTER L SPH EQU $0E ; STACK POINTER H PCL EQU $0F ; 8080 PROGRAM COUNTER L PCH EQU $10 ; 8080 PROGRAM COUNTER H DECIT EQU $11 ; DOUBLE PRECISION -1 INCIT EQU $13 ; DOUBLE PRECISION 0001 FLAG EQU $15 ; USED BY SIMULATOR DESTDA EQU $16 ; DESTINATION DATA DEST EQU $17 ; DESTINATION INDEX SRC EQU $18 ; SOURCE INDEX OPCODE EQU $1A ; CURRENT OPCODE ;PSPEED EQU $1B ; TRACE SPEED FROM PADDLE0 PNT EQU $1C ; DATA POINTER SCR EQU $1E ; SCRATCHPAD REGISTER TEXTTOP EQU $22 ; text window top edge HPOS EQU $24 ; HORIZONTAL CURSOR POSITION VPOS EQU $25 ; VERTICAL CURSOR POSITION CURPOS EQU $28 ; CURSOR POSITION ADDRESS KSWL EQU $38 ; keyboard input routine KSWH EQU $39 ; normally $FD1B HIPCH EQU $FE ; is the PC above 32k limit? KBDBUF EQU $C000 ; KEYBOARD BUFFER KBDSTROBE EQU $C010 ; KEYBOARD STROBE SPKR EQU $C030 ; CLICK LORES EQU $C050 TXTSET EQU $C051 MIXCLR EQU $C052 MIXSET EQU $C053 TXTPAGE1 EQU $C054 TXTPAGE2 EQU $C055 RAMRDOFF EQU $C002 ; Read enable main memory from $0200-$BFFF RAMRDON EQU $C003 ; Read enable aux memory from $0200-$BFFF RAMWRTOFF EQU $C004 ; Write enable main memory from $0200-$BFFF RAMWRTON EQU $C005 ; Write enable aux memory from $0200-$BFFF ALZTPOFF EQU $C008 ; Enable main memory from $0000-$01FF & avl BSR ALTZPON EQU $C009 ; Enable aux memory from $0000-$01FF & avl BSR ; READ HIGH BIT: RAMRD EQU $C013 ; 0=main $0200-$BFFF active reads 1=aux active RAMWRT EQU $C014 ; 0=main $0200-$BFFF active writes 1=aux writes ALTZP EQU $C016 ; 1=aux $0000-$1FF+auxBSR 0=main available AUXMOVE EQU $C311 ; ; The beginning address of the block to be moved must be stored at locations A1L ($3C) and A1H ($3D) and the ending address at ; A2L ($3E) and A2H ($3F). Finally, the destination address must be stored at A4L ($42) and A4H ($43). ; If the carry flag is set, then the move will be performed from main memory to auxiliary memory. ; If it is clear, the move will take place in the opposite direction. CLRLORES EQU $F832 ; clear low res screen PREAD EQU $FB1E ; PREAD returns a number that represents the position of a hand control. SETTXT EQU $FB39 ; Set Text Mode SETGR EQU $FB40 ; mixed text/graphics Mode BASCALC EQU $FBC1 ; calculate base address BELL2 EQU $FBE4 ; 60hz bell VTAB EQU $FC22 ; Sets the cursor vertical position (from CV) HOME EQU $FC58 ; HOME CLREOL EQU $FC9C ; say end of line WAIT EQU $FCA8 ; simple delay KEYIN EQU $FD1B RDKEY EQU $FD0C ; read keyboard input CROUT EQU $FD8E ; Print CR PRBYTE EQU $FDDA ; Prints byte as 2 hex digits COUT EQU $FDED ; character output BELL EQU $FF3A ; outputs ^G OLDRST EQU $FF59 ; set screen mode and init MLI EQU $BF00 ; ProDOS system call OPENCMD EQU $C8 ; OPEN command index READCMD EQU $CA ; READ command index SET_MARKCMD EQU $CE ; SET_MARK index CLOSECMD EQU $CC ; CLOSE command index WRITECMD EQU $CB ; HERE THERE BE DRAGONS ; ---------------------------------------------------------------------------- ; DO AUXMOVE from $9000 to $B000 -> aux $9000 LDA #$00 STA $3C STA $3E STA $42 LDA #$90 STA $3D STA $43 LDA #$B0 STA $3F SEC JSR AUXMOVE ; magic done. JSR HOME JSR CHOOSER ; determine what program/basic/bootloader to load JSR LOADPROGRAM ; load it, based on ACC value JSR HOME JSR INSTRUCTIONS ; copyright and instructions JSR ALTAIRSCREEN ; Draw GR screen ; INIT INIT JMP SETUP ; ---------------------------------------------------------------------------- ; MAIN MNSTRT JMP DISPLAYLOOP ; setup and display first frame ; ---------------------------------------------------------------------------- SIM80 LDA #$00 STA HIPCH ; reset HIPCH for each go-round ; **** add $10 to PCH LDA PCH STA DISPLAYBYTES ; high byte to first bank of LEDs. CMP #$7F ; above 32k limit? BCC ADC10 ROL HIPCH ; carry into HIPCH indicates we schlepped above 32K SEC ; gotta set carry again. SBC #$80 ; subtract #$80 - probably could do AND 7F CLC ADC10 ADC #$10 STA PCH ; **** JSR MAIN ; execute OPCODE ; **** subtract $10 from PCH SEC LDA PCH SBC #$10 LDX HIPCH ; were we reading PC from AUX? BEQ SBC10 ; nope, skip CLC ADC #$80 ROR HIPCH ; HIPCH 1 into carry, leaving 00 SBC10 STA PCH ; store resulting PCH ;**** LDA PSW ; processor status AND #$D7 ; AND 1101 0111 - clear MI and HLTA bits ORA #$02 ; OR 0000 0010 - set WO bit STA PSW ; CLI ; clear interrupts JSR SIMDSK ; operate virtual disk drive RTS ; return ; ---------------------------------------------------------------------------- ; EXECUTE ONE 8080 OPCODE MAIN SEI ; DON'T ALLOW INTERRUPTS CLD ; CLEAR DECIMAL MODE LDX #$00 ; LOAD INDEX ; **** add $10 to SIMH CLC LDA SIMH ; compare to 8f, subtract 80 CMP #$7F ; if it's >=32K boundary after add $10, get from AUX BCC STASIMM2 STA RAMRDON ; set AUX READ SBC #$80 ; subtract #$80 CLC ; read from AUX, still write to MAIN ; add #$80 ; set MAIN READ STASIMM2 ADC #$10 STA SIMH STASIMM LDA (SIML,X) ; FETCH MEMORY *** from (HL) register pair *** STA SIMM ; crashes II if SIMH is C0 softswitch area ; **** subtract $10 from SIMH DECSIMH SEC ; then subtract #$10... oof. LDA SIMH SBC #$10 STA SIMH ;**** LDA RAMRD ; if RAMRD high bit BPL CHECKPCH ; low = RAMRDOFF STA RAMRDOFF ; high = turn it off, etc CLC ; add #$80 to SIMH again LDA SIMH ADC #$80 STA SIMH CHECKPCH ; is PC looking above #$8FFF? LDA HIPCH BEQ LDAPC ; nope, skip STA RAMRDON ; read from AUX LDAPC LDA (PCL,X) ; FETCH INSTRUCTION **** this should come from real $1000-$8fff PHA ; opcode to stack LDA RAMRD ; if RAMRD high bit BPL STAOPCODE STA RAMRDOFF ; high = turn it off, etc STAOPCODE PLA ; get the opcode back STA OPCODE ; EOR #$76 ; TAX ; LDY #$01 ; LDA PNT ; AND INTE ; IS THIS A HALT? BPL CKINT ; STY INTE ; STY PNT ; ORA #$C7 ; STA OPCODE ; DEY ; ; CHECK FOR INTERRUPT CKINT LDA INTE ; BNE NINT ; DEC INTE ; ; ENABLE INTERRUPT NINT TXA ; BNE NOHLT ; DEY ; ; HALT? NOHLT TYA ; BEQ ITR ; JSR INCPC ; ; SET COUNTER/INDEX ITR LDX #$02 ; LDA OPCODE ; DIV PHA ; AND #$07 ; EOR #$07 ; STA ZERO,X ; PLA ; LSR A ; LSR A ; LSR A ; DEX ; BNE DIV ; STX FLAG ; LSR A ; BCS IMI ; BEQ FIRST ; IMARL LDA #$11 ; no, arilog ADC ZERO+$01 ; SIMA TAX ; LDA #$00 ; set dest=A STA ZERO+$01 ; SIMA IMI BEQ MOVE ; SECOND LDA #$07 ; offset for 2nd (page?) FIRST ADC SPEED ; TAX ; Transfer to Index INX ; MOVE TXA ; SET HIGH ADDRESS ASL A ; TAX ; LDA XFERTBL,X ; GET LO ADOR INX ; PHA ; SET LO ADDRESS LDA XFERTBL,X ; PHA ; LDX SPEED ; GET SOURCE INDEX NOFETCH LDY SIMA,X ; GET SOURCE DATA IN Y *** LDX ZERO+$01 ; GET DEST INDEX LDA SIMA,X ; STA ZERO ; SAVE DESTINATION DATA LDA #$44 ; SET 6502 STATUS PHA ; TXA ; DEST IND, IN A & X RTI ; GO INTERPRET ; RTS to stack pointer instruction ; ---------------------------------------------------------------------------- IN LSR A ; BEQ XCHG ; BCC INPUT ; TAY ; JSR CALL65 ; STA SIMA ; RTS ; ; ---------------------------------------------------------------------------- ; CALL 6502 SUBROUTINE CALL65 LDA (PCL),Y ; PHA ; DEY ; BPL CALL65 ; JSR DBLINC ; LDA #$00 ; PHA ; LDA SIMA ; RTI ; ; ---------------------------------------------------------------------------- XCHG JSR RPSCR ; LDX #$04 LDY #$02 JSR RPRP ; LDX #$04 JMP SCRRP ; ; ---------------------------------------------------------------------------- XTHL JSR RPSCR ; JSR SPPNT ; LDX #$02 JSR MEMRP ; LDX #$13 BNE RPMEM ; ; ---------------------------------------------------------------------------- IO LSR A ; which inst? BNE LBL ; STX INTE ; set/clear INTE RTS ; ; ---------------------------------------------------------------------------- LBL LDX #$02 ; point to HL BCC IN ; is it input LSR A ; no, is it xthl? BEQ XTHL ; yes BCS JMPJUMPUN ; is it jump? OUTPUT DEC FLAG ; no, flag an output INPUT JSR IMM ; get port # LDA SRC ; SRC = port number AND #$0F ; clear high bits (FF => 0F) because why keep up with 256 ports when we only need 16? ASL A ; double index for hi/lo byte in table TAX ; port table offset in X LDY #$02 ; IOPLP LDA IOTBLOUT,X ; get IO address STA FLAG,Y ; store in pointer INX ; bump index DEY ; decrement counter BNE IOPLP ; INY ; point to data dir, reg PNTPRT DEY ; point to port LSR FLAG ; FLAG is FF on OUTPUT, 00 on INPUT BCC IOIN ; do input if carry clear. LDA SIMA ; - if carry set, OUTPUT IOOUT STA (DESTDA),Y ; OUTPUT TO PORT JSR SIOOUT ; TELL SIO I HAVE PUT A BYTE TO SEND BCS IOEX ; should return with Carry set??? IOIN INC DESTDA ; switch from PORT-OUT to PORT-IN TYA PHA JSR SIOIN ; TELL SIO I WANT A BYTE - Y=PORT PLA TAY LDA (DESTDA),Y ; input from port STA SIMA ; store in SIMA IOEX RTS ; JMPJUMPUN JMP JUMPUN ; spaghetti ; ---------------------------------------------------------------------------- ARIM JSR IMM ; CLC ; JMP IMARL ; JMPCALLUN JMP CALLUN ; spaghetti ; ---------------------------------------------------------------------------- PUSH LSR A ; IS THIS A PUSH? BCS SKP ; YES, GO PUSH CMP #$03 ; NO, IS THIS A CALL? BEQ JMPCALLUN ; YES, GO CALL JMP UNDEF ; NO, UNDEFINED SKP ASL A ;MAKE AN INDEX TAX ;IS IT PUSH PSW? BNE PUSHT ; NO, KEEP INDEX DEX ;YES, ADJUST INDEX PUSHT TXA ; TEMP SAVE - x=0A on set return, gets PCL/H PHA ; JSR DECSP ; DECREMENT STACK POINTER JSR SPPNT ; IT BECOMES POINTER PLA ; RECOVER TEMP SAVE TAX ; RPMEM LDY #$01 ; CLEAR INDEX ; compare to 8f, THEN add 10 if necessary ;CLC LDA DESTDA,Y ; $04 + $12 = $16 DESTDA STA DISPLAYBYTES ; high byte to first bank of LEDs. CMP #$7F ; over 9000? BCC RPMEM2 ; nope, skip math SBC #$80 ; off by $10 ??? *** STA RAMWRTON ; write to AUX STA DESTDA,Y RPMEM2 CPX #$02 ; **** BNE RPLP ; **** add $10 to SRC+1 ON SHLD, X=2 ; E5 PUSH H AXY=02/02/01 ; 22 SHLD AXY=02/01/01 LDA OPCODE ; is it PUSH H or SHLD? <-- corner case! CMP #$22 BNE STADESTDA LDA DESTDA,Y CLC ; ADD 10 to destination on SHLD ADC #$10 STA DESTDA,Y ; **** STADESTDA LDA RAMWRT ; if writing to aux, add 80 back and write to main BPL RPLP CLC LDA DESTDA,Y ADC #$80 STA DESTDA,Y STA RAMWRTOFF ; write to MAIN RPLP LDA SIMM,X ;GET NEXT RP DATA PHA ; hang onto it LDA DESTDA+1 STA DISPLAYBYTES ; high byte to first bank of LEDs. CMP #$8F ; over 9000 or under 1000? BCS STADESTDA3 ; over 9000 CMP #$10 BCS STADESTDA2 ; Over 1000, skip STADESTDA3 SEC SBC #$80 STA RAMWRTON STA DESTDA+1 STADESTDA2 PLA ; get RP data back STA (DESTDA),Y ;STORE IN MEMORY LDA RAMWRT ; if writing to aux, add 80 back and write to main BPL RPLP2 CLC LDA DESTDA+1 ADC #$80 STA DESTDA+1 STA RAMWRTOFF ; write to MAIN RPLP2 DEX ; DEY ; BEQ RPLP ; CPX #$00 ; **** BNE RTS ; **** reset SRC+1 ON SHLD, X=2 INY INY SEC LDA DESTDA,Y ; $04 + $12 = $16 DESTDA SBC #$10 PHA ; hold that thought... LDA RAMWRT ; writing to AUX? turn that off. BPL RPLP3 CLC LDA DESTDA,Y ADC #$80 RPLP3 STA RAMWRTOFF PLA ; and we're back. STA DESTDA,Y ; **** RTS RTS ; ; ---------------------------------------------------------------------------- JMP CLV ; INDICATE A JUMP CALL JSR CONDIT ; TEST CONDITION BEQ CALLUN ; MET JMP DBLINC ; NOT MET, increment PC ; ---------------------------------------------------------------------------- JUMPUN CLV ; INDICATE A JUMP CALLUN PHP ; preserve overflow, etc CALLUN3 JSR PCPNT ; GET NEXT 2 BYTES CALLUN4 LDX #$13 ; INTO SCRATCH JSR MEMRP ; ; **** add $10 to SRC+1 CLC LDA SRC+1 PHA STA DISPLAYBYTES ; high byte to first bank of LEDs. CMP #$7F ; is above 32k? BCC CALLUN5 LDA #$01 STA HIPCH ; if destination PCH is above 32k? CALLUN5 PLA CLC ADC #$10 ; CLEARS OVERFLOW? STA SRC+1 ;**** PLP ; get overflow, etc. CALLUN2 BVC JM ; JUMP on overflow clear JSR DBLINC ; otherwise? BUMP PC twice SVR LDX #$0A ; SAVE RETURN for RST LDA HIPCH ; return address should be HIPCH as well BEQ SVR1 LDA PCH CLC ADC #$80 STA PCH ; **** subtract 10 before setting return point SVR1 SEC LDA PCH SBC #$10 STA PCH ;**** JSR PUSHT ; ON STACK **** <--- and if SP is >9000? ; **** add 10 after setting return point ??? CLC LDA PCH ADC #$10 STA PCH ;**** JM LDX #$0A ; POINT TO PC ; **** doing a JUMP or CALL - reset HIPCH LDA #$00 STA HIPCH ; **** doing a JUMP or CALL - reset HIPCH JMP SCRRP ; GIVE IT NEW ADDRESS JMPSVR JMP SVR ; SPAGHETTI! ; ---------------------------------------------------------------------------- RETURN JSR CONDIT ; TEST CONDITION BNE RTS ; NOT MET RETUN ; if SPH > 8F or < 10, subtract 80, read from AUX, write to AUX !!! ; **** doing a JUMP or CALL - reset HIPCH LDA #$00 STA HIPCH ; **** doing a JUMP or CALL - reset HIPCH LDA SPH STA DISPLAYBYTES ; high byte to first bank of LEDs. CMP #$8F BCS RETUN1 CMP #$10 BCS RETUN2 SEC RETUN1 SBC #$80 ; *** SPH off by $10? STA RAMRDON STA RAMWRTON STA SPH ; **** add $10 to (SP) before grabbing return point RETUN2 CLC LDY #$01 LDA (SPL),Y ; get byte at (SPL/H) ADC #$10 STA (SPL),Y ;**** LDA RAMRD ; if reading/writing AUX, switch back to MAIN BPL RETUN3 LDA SPH ; return SPH to high value CLC ADC #$80 ; *** SPH off by $10? STA SPH STA RAMRDOFF STA RAMWRTOFF RETUN3 LDX #$0A ; POP RETURN OFF BNE POPIT ; STACK INTO PC RST LDA OPCODE ; CONVERT INSTRUCTION AND #$38 ; TO RESET VECTOR STA SRC ; STORE IT IN SCRATCH LDA #$00 STA HIPCH ; no longer need to inc the PCH if we're resetting to 0038 LDA #$10 ; HIGH RESET VECTOR STA SRC+1 ; ??? SCR+1 ??? BCC JMPSVR ; ; ---------------------------------------------------------------------------- POP LSR A ; POP? BCC OTH ; NO ASL A ; TAX ; POP PSW? BNE POPIT ; NO, KEEP INDEX DEX ; YES, ADJUST INDEX POPIT TXA ; TEMP SAVE INDEX PHA ; JSR SPPNT ; PULL OUT STACK DATA PLA ; BUMP SP TAX ; JSR MEMRP ; SP IS POINTER ICISP JSR ISP ; RECOVER TEMP ISP LDX #$08 ; JMP INC ; ; ---------------------------------------------------------------------------- OTH LDX #$02 ; POINT TO HL LDY #$08 ; ASSUME SPHL ASL A ; IS IT SPHL? BEQ SPHL ; YES, GO DO IT CMP #$04 ; NO, WHAT IS IT? BEQ UNDEF ; UNDEFINED BPL RETUN ; RETURN LDY #$0A ; PCHL ; **** ; if it's a PCHL, add $10 to SIMH to offset subtracting after the operation. LDA SIMH ; **** CLC ; **** ADC #$10 ; **** STA SIMH ; **** SPHL JMP RPRP ; ; ---------------------------------------------------------------------------- CONDIT LSR A ; MAKE INDEX TO MASK. TABLE TAX ; LDA MSKTBL,X ; GET CONDITION FLAG MASK AND PSW ; IS CONDITION SET* BCS RTN ; LEAVE Z FLAG SET EOR MSKTBL,X ; OR REVERSE IT RTN RTS ; ; ---------------------------------------------------------------------------- NOP LDA OPCODE ; CHECK INSTRUCTION BEQ RTN ; Pointer says 00 == NOP UNDEF ;LDY #$0C ; ILLEGAL OPCODE, UNDEFINED ;LDX #$0A ; ;JSR INCDEC ; ;JMP MNSTRT ; ignore and move on. ;JSR BELL ; Beep, then RTS RTS ; RTS on illegal/undefined opcode ? ; ---------------------------------------------------------------------------- DADLXI LSR A ; DROP LSB ASL A ; BNE NSP ; USE SP? LDA #$08 ; YES, POINT TO IT NSP PHA ; SAVE POINTER a=8 TXA ; GET DESTINATION INDEX LSR A ; DAD? BCS LXI ; NO, GO DO LXI DAD PLA ; YES, RECOVER POINTER a=8 TAY ; LDX #$02 ; SET HL AS DESTINATION LDA OPCODE ; **** subtract 10 from SPH before working with it / DAD SP CMP #$39 ; if we're working with stack pointer BNE DAD2 LDA SPH SEC SBC #$10 STA SPH ; **** subtract 10 from SPH before working with it DAD2 JSR INCDEC ; GO ADD RP & HL PHA ; **** if we're working with DAD SP PHP ; preserve carry set, etc LDA OPCODE CMP #$39 BNE CARRY2 LDA SPH ; **** add 10 to SPH after working with it CLC ADC #$10 STA SPH CARRY2 PLP ; reset carry, etc PLA ; **** add 10 to SPH after working with it CARRY TAY ; SAVE RESULT ROL A ; BRING IN CARRY EOR FLAG ; CHG TO BORROW IF SUBTRACT LSR PSW ; LSR A ; RESTORE CARRY(BORROW) ROL PSW ; TYA ; RECOVER RESULT RTS ; ---------------------------------------------------------------------------- LXI JSR PCPNT ; PC INTO PNT PLA ; RECOVER POINTER TAX ; USE TO INDEX DESTINATION RP JSR MEMRP ; MOVE IMMEDIATE DATA TO RP ; if RP is SP, add 10 to SPH ; **** LDA OPCODE CMP #$31 BNE LXI2 CLC LDA SPH ADC #$10 STA SPH ; **** LXI2 JMP DBLINC ; BUMP PROGRAM COUNTER ; ---------------------------------------------------------------------------- LDSTR BIT FOUR ; INDIRECT? BEQ DIRECT ; NO LSR A ; STAX? BCC LDAX ; NO STAX INX ; **** add 10 to DESTDA CLC LDA PSW,X STA DISPLAYBYTES ; high byte to first bank of LEDs. CMP #$7F ; over 9000? BCC STAX1 ; nope, carry on SBC #$80 ; yep, subtract 80 STA RAMWRTON ; write AUX CLC STAX1 ADC #$10 STAX2 STA PSW,X ; store updated destination address DEX ; **** LDA SIMA ; YES,STORE A STA (PSW,X) ; PSW = 04 + 12 = 16 = destDA INX ; **** reset DESTDA SEC LDA PSW,X SBC #$10 LDY RAMWRT ; load Y? Y not!? BPL STAX3 CLC ADC #$80 STA RAMWRTOFF ; back to MAIN STAX3 STA PSW,X DEX ; **** RTS ; ; ---------------------------------------------------------------------------- DIRECT PHA ; TEMP SAVE JSR PCPNT ; PC TO POINTER JSR DBLINC ; LDX #$13 ; JSR MEMRP ; ; **** if LHLD, add 10 to SRC before grabbing memory contents LDA OPCODE CMP #$2A BNE DIRECT2 LDA SRC+1 CLC ADC #$10 STA SRC+1 ; **** if LHLD, add 10 to SRC before grabbing memory contents DIRECT2 LDX #$11 ; JSR SCRRP ; PLA ; RECOVER TEMP LSR A ; LOAD? BCS STORE ; NO, STORE ASL A ; TAX ; BEQ LDAD ; JMP MEMRP ; ; ---------------------------------------------------------------------------- LDAD LDX #$11 ; LDAX CLC ; **** INX LDA SIMA,X STA DISPLAYBYTES ; high byte to first bank of LEDs. CMP #$7F ; over 9000? BCC LDAX1 SBC #$80 STA RAMRDON CLC LDAX1 ADC #$10 STA SIMA,X LDAX2 DEX ; **** LDA (SIMA,X) ; STA SIMA ; SEC ; **** INX LDA SIMA,X SBC #$10 STA SIMA,X LDA RAMRD BPL LDAX3 STA RAMRDOFF ; back to MAIN LDA SIMA,X CLC ADC #$80 STA SIMA,X LDAX3 DEX ; **** RTS ; ; ---------------------------------------------------------------------------- STORE ASL A ; MAKE INDEX TAX ; BEQ STAD ; STORE A DIRECT? JMP RPMEM ; NO, STORE HL DIRECT SHLD STAD LDX #$12 ; POINT TO POINTER JMP STAX ; jump to STAX ; ---------------------------------------------------------------------------- INXDCX LDY #$0C ; SET FOR DECREMENT LSR A ; BCC DRP ; YES LDY #$0E ; NO, SET FOR INCR DRP ASL A ; DROP LSB BNE NOR ; FOR SP? LDA #$08 ; YES, POINT TO SP NOR TAX ; USE AS INDEX JMP INCDEC ; ; ---------------------------------------------------------------------------- DCR SEC ; SET FOR DECREMENT DEC FLAG ; INR LDY #$01 ; SET FOR INCREMENT BTH JSR ADDR ; GO ADD JMP STATUS2 ; SET STATUS, NOT CARRY ; ---------------------------------------------------------------------------- MVI JSR IMM ; GET IMMEDIATE BYTE JMP MOVE ; MOVE IT TO DESTINATION ; ---------------------------------------------------------------------------- MOVIT TYA ; GET SOURCE DATA MVIT1 DEX ; IS DEST MEMORY? BNE NMEM ; NO INX ; **** PHA ; **** LDA SIML,X ; **** SIMH STA DISPLAYBYTES ; high byte to first bank of LEDs. CMP #$7F ; above 8000? ; above 9000 after adding $10 !!! BCC MVIT2 STA RAMWRTON ; set AUX WRITE SBC #$80 ; SBC #$80 CLC ; **** MVIT2 ADC #$10 ; add #$10 ; store in AUX ; add #$80 ; set MAIN WRITE STASIMH STA SIML,X ; **** PLA ; **** DEX ; **** STA (SIML,X) ; YES STORE IN MEMORY INX ; **** PHA ; **** byte into stack LDA RAMWRT BPL STASIMH2 LDA SIML,X STA RAMWRTOFF ; AUX OFF, write to MAIN CLC ADC #$80 STA SIML,X STASIMH2 SEC ; **** LDA SIML,X ; **** SBC #$10 ; **** subtract 10 STA SIML,X ; **** NOMOV PLA ; **** DEX ; **** NMEM STA SIMM,X ; STORE IN DEST REGISTER RTS ; ; ---------------------------------------------------------------------------- ADC CLV ; FLAG W/CARRY BVC ADD ; CMP LDX #$13 ; CHG DEST TO SCR BNE SUB ; SO ONLY PSW IS AFFECTED SBB CLV ; FLAG W/BORROW SUB DEC FLAG ; F L A G = FF FOR SUBTRACT SEC ; ADD JMP ADCRY ; ; ---------------------------------------------------------------------------- ORA TYA ; GET SOURCE DATA ORA SIMA ; LOGICAL OR BCC SOM ; SET FOR A CLEAR AUX CRY XRA TYA ; G E T SOURCE DA T A EOR SIMA ; LOGICAL EXCLUSIVE OR SOM LDY #$00 ; 00 WON'T SET ANY FLAGS BEQ ALL ; GO SAVE RESULT ANA TYA ; G E T SOURCE DA T A PHA ; SAVE IT ORA SIMA ; LOGICAL OR OF BIT ASL A ; T H R E E BECOMES AUX CARRY AND #$10 ; TAY ; PLA ; GET SOURCE DATA AND SIMA ; LOGICAL AND ALL STY SRC+1 ; SAVE FLAG SETTING DATA STA SIMA ; LOGICAL RESULT INTO A JSR STATUS2 ; SET STATUS LDA #$EE ; SELECT STATUS BITS TO CLEAR CYAC AND PSW ; CLEAR SELECTED BITS ORA SRC+1 ; SET SELECTED BITS STPSW STA PSW ; SAVE NEW PSW RTS ; ; ---------------------------------------------------------------------------- ROTATE LDY SIMA ; GET ACCUMULATOR LSR A ; IS THIS CMC OR 8TC? BNE ROCOMP ; NO, IT IS A ROTATE .OR CMA LDA PSW ; YES, CHANGE CARRY EOR #$01 ; CMC BCC STPS ; ORA #$01 ; STC STPS BVS STPSW ; ; ---------------------------------------------------------------------------- ROCOMP BCS LEFT ; LEFT OR RIGHT? LSR A ; CMA? BNE ROT ; NO, ROTATE TYA ; YES, COMMPLEMFNT A EOR #$FF ; STA SIMA ; DOESN* T SET STATUS RTS ; ; ---------------------------------------------------------------------------- ROT TYA ; GET ACCUMULATOR BCS RRC ; LDA PSW ; GET 8080 CARRY RRC LSR A ; TYA ; GET ACCUMULATOR ROR A ; ROTATE RIGHT BVS JCRY ; GO STORE&SET CARRY ; ---------------------------------------------------------------------------- LEFT LSR A ; DECIMAL ADJUST? IYES# GO 00 IT BEQ DAA ; N 0 , GET ACCUMULATOR TYA ; JRLC? BCS RLC ; NO#GET 8080 CARRY LDA PSW ; MOVE IT INTO MSNIB ROR A ; ROR A ; RLC ASL A ; MOVE IT INTO CARRY TYA ; G E T ACCUMULATOR ROL A ; MOVE IT LEFT JCRY STA SIMA ; SAVE IT JMP CARRY ; GO SET STATUS ; ---------------------------------------------------------------------------- DAA CLC ; PHP ;PRE3ERVE STATUS | TYA ;GET SOURCE DATA STA ZERO ; PREP FOR ADD AND #$0F ;LOOK AT LSNI8 ADC #$06 ;IF *0A WILL CAUSE AUX CRY ORA PSW ;OR I F AC I S ALREADY SET AND #$10 ; EITHER SET? BEQ NOSIX ;NO, DON T ADJUST LSNIB LDA #$06 ; YES# ADJUST IT NOSIX TAY ; ADC ZERO ; G E T SOURCE BCS SXTY ; ADC #$60 ;113 MSNIB NOW > A0T IIF SO* CARRY IS SET ROL A ;G E T CARRY ORA PSW ;OR WITH 8080 CARRY LSR A ;I S EITHER SETT BCC DA ;NO# DON'T ADJUST MSNIB l SXTY TYA ;YES* ADJUST MSN!B I ADC #$5F ;(5F + CARRY = 60) TAY ; DA LDX #$00 ;DESTINATION IS A PLP ;RESTQRE STATUS ADCRY JSR ADDR ; JSR CARRY ; STATUS2 TAY ; SAVE RESULT ROL PSW ; CLEAR P3W SIGN BIT ASL A ; PUT NEW SIGN IN CARRY STA FLAG ; C L E A R L S B OF F L A G LDA PSW ; PUT NEW SIGN IN P3N ROR A ; SGN ORA #$46 ; PRESET Z*P 8 PRESET TAX ; 3AVE IN X TYA ; RECOVER WORD BEQ DONE ; I F ZERO*ALL DONE FLIP INC FLAG ; IFLIP FLAG | PAR LSR A ; TEST EACH BIT I BEQ ALL2 ; NO MORE BITS BCC PAR ; ALL2 BCS FLIP ; LSR FLAG ; TEST FLAG TXA ; RECOVER P3w AND #$BF ; CLEAR Z BCS REC ; PARITY EVENT AND #$FB ; N0* CLEAR P REC TAX ; BACK TO X DONE STX PSW ; STORE AS PSW RTS ; ; ---------------------------------------------------------------------------- PCPNT LDX #$0A ; **** 0A + 05/06 = 0F = PCL/PCH RPPNT LDY #$11 ; 05 + 11 = 16 = store at DESTDA BNE RPRP ; never zero, branch always RPSCR LDY #$13 ; store at SRC instead ; moves scratchpad or other RP bytes to PC, vice versa RPRP LDA SIMA,X ; load SOURCE BYTE L STA SIMA,Y ; store at DESTINATION L LDA SIMM,X ; load SOURCE BYTE H CPY #$08 ; **** if SPHL BNE RPRP4 ; **** CLC ; **** ADC #$10 ; **** JMP RPRP2 RPRP4 CPX #$0A ; X=0A, PC IS POINTER. BNE RPRP2 LDA HIPCH ; HIPCH = 1, inc resulting pointer by 70 BEQ RPRP3 CLC LDA SIMM,X ADC #$80 ; 70 or 80? oof... JMP RPRP2 RPRP3 LDA SIMM,X RPRP2 STA SIMM,Y ; store at DESTINATION H RPRTS RTS ; ; ---------------------------------------------------------------------------- SCRRP TXA ; RP is destination (RP is a Register Pair) TAY ; 0A on CALL (for return address???) LDX #$13 ; SCR is source BNE RPRP ; never zero, branch always SPPNT LDX #$08 ; SP is Source BNE RPPNT ; jump RPPNT DBLINC JSR INCPC ; Increments PC twice ; ADVANCE COUNTER INCPC LDX #$0A ; INC PC - $0A + $05 = $0F = PCL INC LDY #$0E ; point to INC data - $0E + $05 = $13 = INCIT (0001) INCDEC CLC ; INDE LDA SIMA,X ; get register/PC value ADC SIMA,Y ; add value from STA SIMA,X ; save INY ; bump indexes INX ; TYA ; check index 0E->0F AND #$01 ; last pass? - increment HI byte BNE INDE ; not done, loop RTS ; yes ; ---------------------------------------------------------------------------- DECSP JSR DSP ; DEC SP twice DSP LDX #$08 ; DEC SP DEC LDY #$0C ; point to DEC data BNE INCDEC ; go do it ; ---------------------------------------------------------------------------- MEMRP TYA ; grab source pointer offset LDA DESTDA+1 STA DISPLAYBYTES ; high byte to first bank of LEDs. CMP #$8F ; *** off by $10 *** WTF *** BCS AUXMEMRP CMP #$10 ; less than $10, rolled over from $F0 BCS MRLP1 AUXMEMRP STA RAMRDON ; read from AUX SEC SBC #$80 STA DESTDA+1 MRLP1 LDY #$01 ; move data in memory MRLP LDA (DESTDA),Y ; get next byte STA SIMM,X ; store in destination DEX ; bump indices DEY ; BEQ MRLP ; LDA RAMRD ; if we're still reading from AUX, add 80 back BPL MRLPRTS STA RAMRDOFF ; read from MAIN LDA DESTDA+1 CLC ADC #$80 STA DESTDA+1 MRLPRTS RTS ; ; ---------------------------------------------------------------------------- IMM LDX #$00 ; GET BYTE POINTED AT ; BY PROGRAM COUNTER LDA HIPCH ; if HIPCH, get it from AUX BEQ IMM2 STA RAMRDON ; AUX on IMM2 LDA (PCL,X) STA SRC ; SAVE IT IN SCR LDA #$13 ; MAKE SCR THE SOURCE INDEX STA SPEED ; ??? should SPEED be SCR? STA RAMRDOFF ; back to main JSR INCPC ; BUMP PC LDX #$00 ; POINT TO INTERP PAGE 2 RTS ; ; ---------------------------------------------------------------------------- ADDR TYA ; EOR FLAG ; TAY ; AND #$0F ; STA SRC ; ROL A ; BVS WOCRY ; EOR PSW ; WOCRY PHA ; LSR A ; LDA ZERO ; AND #$0F ; ADC SRC ; AND #$10 ; STA SRC+1 LDA #$EF ; JSR CYAC ; PLA ; LSR A ; TYA ; ADC ZERO ; JMP MVIT1 ; ; ---------------------------------------------------------------------------- ; TABLE OF PSW MASKS MSKTBL DB $80 ; SIGN BIT MASK FOUR DB $04,$01,$40 ; PARITY BIT MASK, CARRY BIT MASK, ZERO BIT MASK ; XFER TABLE XFERTBL DB >MOVIT,ROTATE,MVI,DCR,INR,INXDCX,LDSTR,DADLXI,NOP,RST,ARIM,PUSH,CALL,IO,JMP,POP,RETURN,CMP,ORA,XRA,ANA,SBB,SUB,ADC,ADD, current HPOS, BCS INCHPOS ; increment HPOS and start over LDA #$FF ; otherwise, STA HPOS ; set HPOS to FF BNE INCHPOS ; then increment to 00 ; ---------------------------------------------------------------------------- ; I/O TABLE ; 0/10 = SIO STATUS = for left bank of switches ; 1/11 = SIO DATA = to put a byte at lower left of screen ; 08 = Disk Enable/Status channel ; 09 = Disk control/sector position channel ; 0A = Disk data channel ; FF/0F = panel switches IOTBLOUT DB >SIOSTATUSOUT,SIODATAOUT,DSKSELECT,DSKCONTROL,DSKWRITE,DISPLAYBYTES,SIOSTATUSIN,SIODATAIN,DSKSTATUS,DSKSECTORIN,DSKREAD,SWITCHBYTES, ignore for now AND #$0F ; 1x => 0x CMP #$01 ; is it on port 1 or 11? BNE DSKOUT ; if not, is it output to DSK? ; LOAD BYTE FROM SIODATA LDA SIODATAOUT ; load SIO DATA ; translate CR/LF into newline. ; CR = 0D ; LF = 0A CMP #$0A BEQ SIORTS ; ignore LF, just do CR. ORA #$80 ; add the high bit? ; OUTPUT TO SCREEN JSR COUT ; output char SIORTS SEC RTS SIOIN ; SIGNALLED FROM IOIN THAT SIM80 LOOKING FOR A BYTE. ; looking for a byte from IN 1 or 0? LDX #$00 LDA SRC ; if input is 00, checking SIO status. CMP #$ff ; sense switches. BEQ SENSEIN CMP #$0A ; looking for DISK DATA BEQ DSKIN CMP #$09 ; looking for DISK SECTOR BEQ DSKSPIN ; "spin" the disk CMP #$10 ; SIO2 on port 10/11. SIO1 on port 0/1 BCC SIO1IN INX INX ; use SIO2 status bytes instead of SIO1 JMP SIO2IN SIO1IN ;AND #$0F ; kill hi nibble so 10==00 BNE SIOKEY ; 00? status check. Otherwise, expects a byte. STA SIODATAIN ; clear the pipes on status check, 00 to data JMP SIOREADY ; set status to ready for I/O ; CHECK KEYBOARD SIOKEY LDA KBDBUF ; read keyboard STA KBDSTROBE ; clear strobe ; IF KEY WAITING, LOAD IT IN SIODATA BPL SIORTS ; no key. RTS. ; BPL CHECKKSW ; if bit8/carry, has keyboard input. else check for BASIC LOAD STA SIODATAIN ; put ascii in DATA ; SET SIOSTATUS TO 00 = HAVE DATA LDA SIOSTATUSBYTES,X ; set STATUS to 00 - HAVE DATA/READY FOR OUTPUT STA SIOSTATUSIN RTS ; NO KEY WAITING ; SET SIOSTATUS 01 = STILL WAITING INPUT ;CHECKKSW JSR KSW ; CHECK FOR KSW INPUT FROM BASIC LOAD ; ; should only run on input and if src = 1? ; BPL SIORESET ; if high bit set, have a byte from BASIC LOAD. otherwise, return SIOREADY ; SET SIOSTATUS TO 00 = HAVE DATA LDA SIOSTATUSBYTES,X ; set STATUS to 00 - HAVE DATA/READY FOR OUTPUT STA SIOSTATUSIN RTS SIORESET INX LDA SIOSTATUSBYTES,X ; 01 = WAITING FOR INPUT/READY FOR OUTPUT STA SIOSTATUSIN ; RTS SENSEIN AND #$0F CLC ROL TAY LDA IOTBLIN,Y ; sense switches up/down don't have an IN/OUT version STA DESTDA+1 INY LDA IOTBLIN,Y STA DESTDA RTS SIO2IN AND #$0F ; #$10 -> #$00 BEQ SIO2STATUS ; SIO2 data input LDA KBDBUF ; get the key STA KBDSTROBE ; clear the strobe STA SIODATAIN ; store it in datain buffer JMP SIORESET ; input done. set status to waiting for next check. SIO2STATUS ; looking for status of SIO2 - have a byte, byte sent, or 00 waiting LDA KBDBUF ; if KBDBUF bit 8 hi, have a byte BPL SIORESET ; bit8 lo, set status to byte sent/waiting JMP SIOREADY ; bit 8 hi, has keyboard input. DSKOUT CMP #$0A ; port 0A -> output to DSK BNE SIORTS ; not 0A? -> ignore LDY SECTOROFFSET ; which byte of the current sector to push to LDA TRACKPOINTER ; offset within track ($89xSECTOR) STA $0 LDA TRACKPOINTER+1 STA $1 LDA DSKWRITE ; load the byte STA ($0),Y ; set byte at track pointer + offset INC SECTOROFFSET ; ready for next read RTS ; ---------------------------------------------------------------------------- FULLSCREEN ASC " FULL SCREEN MODE: CONTROL-C TO EXIT ",00 LASTLINEBUF DS 41,$00 ; 40 bytes to store last line of split screen text. followed by zero to stop. CTRLF LDA TEXTTOP ; control-F for full-screen terminal BEQ CTRLFRTS ; already in full-screen. ignore. LDX #$27 ; copy last line of text from $7d0 BUFFERLINE LDA $7D0,X STA LASTLINEBUF,X DEX BPL BUFFERLINE BIT TXTSET ; set text Mode LDA #$00 STA TEXTTOP ; to make scroll full screen. JSR HOME ; clear low res screen ; Ask for 80 columns? JSR ASK80 ; Returns with ZERO on YES BNE CTRLFGO LDA #$00 ; clears out the Y/N from accumulator JSR $C300 CTRLFGO LDY #$3F JSR $FE86 LDA #$00 STA $F3 ; no blinking text? LDY #>FULLSCREEN LDA #LASTLINEBUF LDA #ASK80STRING LDA #/= 8, and we do SWITCHBYTES+0 carry set LDA #$00 ; A < 8, and we do SWITCHBYTES+1 carry clear ADC #$00 ; if carry, 0. If not, 1 EOR #$01 ; flip bit 1 CMP #$01 ; carry set, >7, so needs an extra loop BCS SETBANK ; carry clear, 0-7 INX SETBANK TAY ; bank 0 or 1 SEC ; set carry bit to rotate into accumulator LDA #$00 ROTATEIN ROL ; rotate the bit into DEX BNE ROTATEIN EOR SWITCHBYTES,Y STA SWITCHBYTES,Y JSR UPDATESWITCHES ; update the display RTS ; --------------------------------------------------------------------------- ; Display bytes as sWITCHES UPDATESWITCHES BIT SPKR ; click LDA #$06 ; down row to use COUT JSR BASCALC LDA #$06 ; over for COUT STA HPOS LDX #$00 ; PC HI JSR SWITCHLOOP LDA #$06 ; down row to use COUT JSR BASCALC LDX #$01 ; PC LO JSR SWITCHLOOP RTS SWITCHLOOP LDY #$08 ; do a byte's worth LDA SWITCHBYTES,X ; put the switch status into scratch STA SWITCHSCRATCH,X SWLOOP LDA #$AA ; gray JSR COUT ; output gray between switches LDA #$25 ; switch DOWN CLC ; clear carry ROL SWITCHSCRATCH,X ; rotate opcode, hi bit into carry BCC SETSW ; if Carry clear, skip LDA #$26 ; switch UP SETSW JSR COUT ; output to screen DEY ; done all 8 bits? BNE SWLOOP ; bottom pixels LDA SWITCHBYTES,X ; put the switch status into scratch STA SWITCHSCRATCH,X LDA #$07 ; down row to use COUT JSR BASCALC LDA HPOS ; over for COUT SEC SBC #$10 STA HPOS SWITCHLOOP2 LDY #$08 ; do a byte's worth LDA SWITCHBYTES,X ; put the switch status into scratch STA SWITCHSCRATCH,X SWLOOP2 LDA #$AA ; gray JSR COUT ; output gray between switches LDA #$A6 ; switch DOWN CLC ; clear carry ROL SWITCHSCRATCH,X ; rotate opcode, hi bit into carry BCC SETSW2 ; if Carry clear, skip LDA #$A5 ; switch UP SETSW2 JSR COUT ; output to screen DEY ; done all 8 bits? BNE SWLOOP2 BIT SPKR ; click RTS ; --------------------------------------------------------------------------- BYTELOOP LDY #$08 ; do a byte's worth BITLOOP LDA #$AA ; gray JSR COUT ; output gray between lights LDA #$A8 ; LED OFF CLC ; clear carry ROL DISPLAYBYTES,X ; rotate opcode, hi bit into carry BCC SETLED ; if Carry clear, skip LDA #$A1 ; LED ON SETLED JSR COUT ; output to screen DEY ; done all 8 bits? BNE BITLOOP RTS WAITLED LDY #$A8 ; light off BCC SETWAIT ; Carry clear, lamp off LDY #$A1 ; Carry set, light on SETWAIT LDA #$03 ; down one row to use COUT JSR BASCALC LDA #$02 ; over 22 for COUT STA HPOS TYA JSR COUT ; output to screen RTS ; ---------------------------------------------------------------------------- BOTTOMROW ; flip switches ; six switches X = number of loops ; Y =1 for up, =0 for down. BIT SPKR ; click LDA #$04 ; start at column 4 STA HPOS BRSWLOOP ; bottom row switch loop. INC HPOS INC HPOS ; over two spaces INC HPOS ; over two spaces DEX ; loop X times BNE BRSWLOOP ; switch last one up/down LDA #$0A ; row 10 JSR BASCALC LDA #$55 ; #$55 is down CPY #$0 ; is Y 0 or 1? BEQ SWDOWN1 LDA #$52 ; #$52 is up SWDOWN1 JSR COUT ; put switch pixels LDA #$0B ; Down to row 11 JSR BASCALC DEC HPOS ; back over 1 px LDA #$A2 ; #$A2 is down CPY #$0 ; is Y 0 or 1? BEQ SWDOWN2 LDA #$A5 ; #$A5 is up/center SWDOWN2 JSR COUT ; put switch pixels BIT SPKR ; click LDA #$80 JSR WAIT ; delay to show switch action. RTS BOTTOMROWRST LDX #$04 ; four of six switches X = number of loops LDA #$05 ; start at column 4 STA HPOS BRRSLOOP LDA #$0A ; row 10 JSR BASCALC INC HPOS INC HPOS ; over two spaces LDA #$25 ; #$25 is centered JSR COUT ; put switch pixels LDA #$0B ; Down to row 11 JSR BASCALC DEC HPOS ; back over 1 px LDA #$A5 ; #$A5 is center JSR COUT ; put switch pixels DEX ; loop X times BNE BRRSLOOP ; switch last one up/down RTS ; --------------------------------------------------------------------------- PRLOHI JSR PRBYTE ; prints a pair of registers TXA ; PRREG JSR PRBYTE ; print hex of one register, followed by space (#$A0) LDA #$A0 ; JMP $FDED ; JMP to COUT with RTS ? MOVETXT ; move the current text page to alt text page LDA #$00 ; Setup pointers to move memory STA $3C ; $3C and $3D for source start LDA #$04 STA $3D LDA #$FF STA $3E ; $3E and $3F for source end LDA #$07 STA $3F ; LDA #$00 STA $42 ; $42 and $43 for destination LDA #$08 ; $1000 == destination STA $43 LDA #$00 ; Clear ACC, X,Y for smooth operation TAX TAY JSR $FE2C ; F8ROM:MOVE ; Do the memory move RTS ALTAIRSCREEN ; move graphic data to $4000 LDA ALTAIRLO ; Setup pointers to move memory STA $3C ; $3C and $3D for source start LDA ALTAIRHI STA $3D LDA ALTAIRLO STA $3E ; $3E and $3F for source end LDA ALTAIRHI CLC ADC #$04 ; add $400 to start == end of graphic STA $3F ; LDA #$00 STA $42 ; $42 and $43 for destination LDA #$50 ; $4000 == destination STA $43 LDA #$00 ; Clear ACC, X,Y for smooth operation TAX TAY JSR $FE2C ; F8ROM:MOVE ; Do the memory move LDA #$15 ; Kill 80-Column mode whether active or not JSR $FDED ; F8ROM:COUT STA $C050 ; rw:TXTCLR ; Set Lo-res page 1, mixed graphics + text STA $C053 ; rw:MIXSET STA $C054 ; rw:TXTPAGE1 STA $C056 ; rw:LORES ; display the data from $4000 at $400 RESETVPTR LDA #$00 ; Move titlepage from $4000 to $400 (screen) STA $FE ; pointer for where we are at vertically on screen TAY ; Y-Reg used for indexing across (horiz) screen VERTICALPTR LDA $FE ; pointer for where we are at vertically on screen JSR $F847 ; F8ROM:GBASCALC LDA $26 STA $FA ; $FA is our offset GBASL Byte (Source data titlepage) LDA $27 ; Add 04 w/ Carry to get to $4000 where graphic data is ADC #$4C STA $FB ; $FB is our offset GBASH Byte (Source data titlepage) GRABSTORAGE LDA ($FA),Y ; Grab from storage STA ($26),Y ; Put to screen INY CPY #$28 ; #$28 past the width of screen? BNE GRABSTORAGE ; No? Back for another round LDA #$00 TAX TAY INC $FE ; Next line down vertically LDA #$00 TAX TAY LDA $FE CMP #$15 ; #$18 bottom of screen - go to line 21 for legend BNE VERTICALPTR ; No? Go back and do next line down LDA #$00 STA KBDSTROBE ; r:KBDSTRB ; Clear keyboard strobe ; LOOP HERE TO WAIT FOR KEYPRESS ??? RTS ; We now return you to your regular programming ;/ALTAIRSCREEN ************************************************** * writes instructiONS ************************************************** COPYRIGHT ASC "APPLE-80 COPYRIGHT 1979 BY DANN MCCREARY",00 ; ascii copyright info LINE1 ASC " SIM8800 BY CHARLES MANGIN, 2019", 00 LINE2 ASC "KEYBOARD COMMANDS:",00 LINE3 ASC " " INV '0-9' ASC ", " INV 'A-F' ASC " TOGGLES SWITCHES 0-15",00 LINE4 ASC " " INV 'X' ASC " TO EXAMINE MEMORY",00 LINE5 ASC " " INV 'N' ASC " FOR NEXT ADDRESS, " INV '^' ASC " FOR PREVIOUS",00 LINE6 ASC " " INV 'P' ASC " TO DEPOSIT BYTE INTO MEMORY",00 LINE6A ASC " " INV 'O' ASC " TO DEPOSIT NEXT BYTE",00 LINE7 ASC " " INV 'SPACE' ASC " OR " INV 'S' ASC " TO EXECUTE 1 STEP",00 LINE8 ASC " " INV 'T' ASC " TO START INTERACTIVE TRACE",00 LINE9 ASC " " INV 'G' ASC " TO RUN PROGRAM",00 LINE9A ASC " " INV 'CTRL-C' ASC " TO STOP RUN/TRACE",00 LINE9B ASC " " INV 'CTRL-F' ASC " TO RUN FULL SCREEN",00 LINE10 ASC " " INV 'K' ASC " TO SET BREAKPOINTS",00 LINE11 ASC " " INV 'R' ASC " TO EDIT REGISTERS",00 LINE12 ASC " " INV 'I' ASC " FOR THESE INSTRUCTIONS",00 ANYKEY ASC " PRESS SPACE TO CONTINUE",00 INSTRUCTIONSTBL DB #>LINE2,#LINE3,#LINE4,#LINE5,#LINE6,#LINE6A,#LINE7,#LINE8,#LINE9,#LINE9B,#LINE9A,#LINE10,#LINE11,#LINE12,#CHOOSER1,CHOOSER2,CHOOSER3,CHOOSER4,CHOOSER5,CHOOSER6, 0x TAX RTS SHOWINST STA TXTPAGE2 ; alternate page JSR SETTXT ; sets text mode INSTLOOP LDA KBDBUF ; check for keydown CMP #$A0 BEQ RETURNSIM ; JMP INSTLOOP ; got a key? RETURNSIM STA KBDSTROBE ; clear key STA LORES STA MIXSET STA TXTPAGE1 ; main text/gr page JMP DISPLAYLOOP ; back to it. INSTRUCTIONS LDA #$00 STA VPOS JSR VTAB LDY #>COPYRIGHT LDA #LINE1 LDA #ANYKEY LDA #PAPERTAPE,>FILENAME1,>FILENAME2,>FILENAME3,>FILENAME4,>FILENAME5 READLOCLO DB BASICBUFFER,$10,$10,$10,$10,$10 ; remember to add $10 ************************************************** LOADPROGRAM BEQ NOPROGRAM ; x=0, no program to load. JSR BLOAD ; loads BASIC LDA BASICBUFFERLO ; locate BASICBUFFERLO CLC ADC TRANSFERRED ; ADD TRANSFERRED STA $00 ; put BASICBUFFERLO,$00 LDA BASICBUFFERHI ; add BASICBUFFERHI + TRANSFERRED+1 ADC TRANSFERRED+1 ; put BASICBUFFERHI,$01 STA $01 LDY #$00 ; Y=0 TYA STA ($00),Y ; put #$00 ($0),Y NOPROGRAM RTS ; Otherwise done ************************************************** BLOAD ; file gets chosen by LDX before JSR BLOAD LDA FILENAMESLO,X STA OPENFILENAME LDA FILENAMESHI,X STA OPENFILENAME+1 LDA READLOCLO,X STA READLOC LDA READLOCHI,X STA READLOC+1 JSR OPEN ;open "DATA" JSR READ BNE ERROR ; error if return !=0 JSR CLOSE BNE ERROR ; error if return !=0 RTS OPEN JSR MLI ;Perform call DB OPENCMD ;CREATE command number DW OPENLIST ;Pointer to parameter list BNE ERROR ;If error, display it LDA REFERENCE STA READLIST+1 STA CLOSELIST+1 RTS READ JSR MLI DB READCMD DW READLIST RTS CLOSE JSR MLI DB CLOSECMD DW CLOSELIST RTS OPENLIST DB $03 ; parameter list for OPEN command OPENFILENAME DB $00,$00 DA MLI-$400 ; buffer snuggled up tight with PRODOS REFERENCE DB $00 ; reference to opened file READLIST DB $04 DB $00 ; REFERENCE written here after OPEN READLOC DB $00,$00 ; write to end of sim8800 DB $FF,$FF ; read as much as $FFFF - should error out with EOF before that. TRANSFERRED DB $00,$00 CLOSELIST DB $01 DB $00 ERROR JSR PRBYTE ;Print error code JSR CROUT ;Print a carriage return JSR BELL RTS ************************************************** DSKLOAD JSR DSKOPEN ; open dsk file JSR SETDSKOFFSET ; SET_MARK JSR READDSK BNE ERROR ; error if return !=0 JSR CLOSE BNE ERROR ; error if return !=0 RTS DSKOPEN JSR MLI ;Perform call DB OPENCMD ;CREATE command number DW DSKOPENLIST ;Pointer to parameter list BNE ERROR ;If error, display it LDA DSKREFERENCE STA DSKREADLIST+1 STA CLOSELIST+1 STA MARKLIST+1 RTS SETDSKOFFSET ;LDA DSKTRACK ; get track number ;BEQ RESETOFFSET ; if zero, rezero MARK and move along LDA #$00 RESETOFFSET STA DSKOFFSET ; rezero the offset STA DSKOFFSET+1 STA DSKOFFSET+2 LDX DSKTRACK ; how many loops to INC offset by? BEQ SET_MARK ; zero = skippity MARKLOOP LDA DSKOFFSET ; should be zero to start *** magic happens *** CLC ADC #$20 ; multiply $1120 by track number STA DSKOFFSET LDA DSKOFFSET+1 ; store result in DSKOFFSET ADC #$11 STA DSKOFFSET+1 BCC MARKLOOP2 INC DSKOFFSET+2 MARKLOOP2 DEX ;*** magic happens *** BNE MARKLOOP SET_MARK JSR MLI DB SET_MARKCMD DW MARKLIST BNE ERROR RTS READDSK JSR MLI DB READCMD DW DSKREADLIST RTS OUTTODSK JSR DSKOPEN ; open dsk file JSR SETDSKOFFSET ; SET_MARK JSR WRITEDSK BNE ERROR ; error if return !=0 JSR CLOSE BNE ERROR ; error if return !=0 RTS DSKOPENLIST DB $03 ; parameter list for OPEN command DSKOPENFILENAME DB DSKFILE DA MLI-$400 ; buffer snuggled up tight with PRODOS DSKREFERENCE DB $00 ; reference to opened file MARKLIST DB $02 ; 2 parameters for set_mark DB $00 ; mark REFERENCE DSKOFFSET DB $00,$00,$00 ; 3 bytes, lo to hi DSKREADLIST DB $04 DB $00 ; REFERENCE written here after OPEN DSKREADLOC DB BASICBUFFER ; write to end of sim8800 DB $20,$11 ; read $1120 bytes at a time (137*32) DSKTRANSFERRED DB $00,$00 DSKFILE DB ENDDSKNAME-DSKNAME ;Length of name DSKNAME ASC '/SIM8800/ZORK.DSK' ;followed by the name ENDDSKNAME EQU * WRITEDSK JSR MLI DB WRITECMD DW DSKREADLIST ; same parameters for read/write RTS ************************************************** ; Defaults: ; DSKTRACK == 0 ; set DSKSTATUS = A5 : *** 1 == FALSE, 0 == TRUE *** ; bit 0 = not ready to write 1 ; bit 1 = head move allowed 0 ; bit 2 = head status not on 1 ; bit 5 = interrupt disabled = 1 ; bit 6 = 0 - on track 0 ; bit 7 = 1 - data not ready yet ; ; set DSKSECTORIN = 0 ; ; on DSKCONTROL out - if bit 2 = 1, then load 137 bytes from DSKTRACK. ; ; OPEN DISKBASIC41, SET_MARK = DSKTRACK, READ 32*137 bytes into BASICBUFFER ; ; ************************************************** DSKSELECT DB $00 ; port 08 out - latches and enables drive - ignored for now, only enabling 1 drive (0) DSKSTATUS DB $A5 ; port 08 in - indicates drive status 1010 0101 *** 0=TRUE, 1=FALSE *** ; not ready to write, move enable, head off, n/a, n/a, interrupt disable, on track 0, no data DSKCONTROL DB $00 ; port 09 out - controls drive functions *** 1=TRUE, 0=FALSE *** DSKSECTORIN DB $00 ; port 09 in - indicates disk sector position - start at 00 ; 0-31 offset by 1 bit, bit 0=0 when on sector 0, 1 otherwise. DSKWRITE EQU $FA ; port 0A out - data to write DSKREAD EQU $FB ; port 0A in - data to read DSKTRACK DB $00 ; 32 sectors ($20) per track. Start at track 00 (4K=$1120 per track) DSKSECTOR DB $00 ; 137 bytes ($89) per sector. Start at sector 00 ; *** use this for math, DSKSECTORIN for comms. SECTOROFFSET DB $00 ; how far into the sector to grab bytes TRACKPOINTER DB $00,$00 ; how far into the track to look for sector start ************************************************** SIMDSK LDA DSKCONTROL ; see what the last operation wants from the drive BEQ DSKRTS ; ZERO? Do nothing. TAX ; hang onto DSKCONTROL byte AND #$03 ; one/two LO bits set? 00000011 BNE MOVEHEAD ; moving head in/out TXA ; get DSKCONTROL back CMP #$08 ; bits 2/3 set, load/unload head 00001000 BEQ UNLOADHEAD CMP #$80 BEQ WRITESEQUENCE ; == #$80 for DSK write??? AND #$04 ; == 04 TRUE = load HEAD BEQ DSKRTS ; anything else? LOADHEAD LDA DSKSTATUS ; if bit 0=0 then, yes. Write was initiated at least. TAX AND #$01 ; if it's 1, no need to write BNE LOADHEAD2 TXA ; otherwise, write the track to DSK file ORA #$01 STA DSKSTATUS ; set bit 0 to disable write for next pass JSR OUTTODSK ; write the track with MLI LOADHEAD2 JSR DSKLOAD ; LOAD TRACK TO BUFFER AFTER WRITING CURRENT TRACK BACK TO DISK LDA DSKSTATUS ; DSKCONTROL == #$04 AND #$7B ; 0111 1011B .....0.. = head on, data waiting ; ORA #$02 ; 0000 0010B head not movable - unnecessary STA DSKSTATUS ; move off, head on, JMP DSKRTS UNLOADHEAD LDA DSKSTATUS ; DSKCONTROL == #$08 ORA #$04 ; 00000100B .....10. = move on, head off AND #$FD ; 11111101B STA DSKSTATUS DSKRTS LDA #$00 STA DSKCONTROL ; reset DKSCONTROL to prevent loopity RTS WRITESEQUENCE LDA DSKSTATUS ; unset bit 0 to enable write AND #$FC ; set head loaded true. STATUS bit 2 = 0 STA DSKSTATUS ; 11111100 ; set sector status true. SECTOR bit 0 = 0 ; bytes OUT on 0A JMP DSKRTS MOVEHEAD ; DID WE WRITE TO THIS TRACK? LDA DSKSTATUS ; if bit 0=0 then, yes. Write was initiated at least. TAX AND #$01 ; if it's 1, no need to write BNE MOVEHEAD2 TXA ; otherwise, write the track to DSK file ORA #$01 STA DSKSTATUS ; set bit 0 to disable write for next pass JSR OUTTODSK ; write the track with MLI MOVEHEAD2 LDA DSKCONTROL ; move which way? ROR ; bit 0 into carry BCS STEPIN ; bit 0 set, move to HIGHER track # STEPOUT LDA DSKTRACK ; if already on track 0, ignore. BEQ SETTRACK0 ; make sure TRACK0 bit is set? DEC DSKTRACK ; otherwise next lower track BEQ SETTRACK0 ; JSR DSKLOAD ; LOAD TRACK N TO BUFFER JMP DSKRTS SETTRACK0 LDA DSKSTATUS ; AND #$BF ; 10111111 unset bit 6 (track 0=TRUE) STA DSKSTATUS JSR DSKLOAD ; LOAD TRACK 0 TO BUFFER JMP DSKRTS STEPIN LDA DSKTRACK CMP #$4D ; can't go higher than 77 BEQ DSKRTS INC DSKTRACK LDA DSKSTATUS ORA #$40 ;01000000 - sets bit 6, not on TRACK0 STA DSKSTATUS JSR DSKLOAD ; LOAD TRACK N TO BUFFER JMP DSKRTS ************************************************** ALTAIRLO DB ALTAIRSCREENDATA ALTAIRTABLE DA ALTAIRLO,ALTAIRHI ALTAIRSCREENDATA HEX 66,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,A6,66 HEX 66,AA,AA,AA,AA,AA,AF,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,AA,66 HEX 66,FF,F5,FF,F5,FF,F5,F5,F5,FF,F5,FF,FF,F5,FF,F5,FF,F5,FF,F5,FF,F5,FF,FF,F5,FF,FF,FF,F5,FF,FF,FF,F5,FF,FF,FF,F5,FF,FF,66 HEX 00,00,00,00,00,00,00,00,66,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,A1,AA,A1,AA,A1,AA,A1,AA,A1 HEX AA,A1,AA,A1,AA,A1,AA,66,66,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA HEX AA,AA,AA,AA,AA,AA,AA,66,66,6A,6A,6A,6A,6A,6A,6A,6A,6A,6A,6A,6A,6A,6A,6A,6A,6A,6A,6A,6A,6A,6A,6A,6A,6A,6A,6A,6A,6A,6A,6A HEX 6A,6A,6A,6A,6A,6A,6A,66,00,00,00,00,00,00,00,00 HEX 66,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AA,AF,AA,AF,AA,AF,AA,AF,AA,AF,AA,AF,AA,AF,AA,AF,AA,66 HEX 66,AA,25,AA,AA,AA,AA,25,AA,AA,25,AA,AA,25,AA,AA,25,AA,AA,25,AA,AA,25,AA,AA,AA,AA,25,AA,AA,AA,25,AA,AA,AA,AA,AA,AA,AA,66 HEX 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00 HEX 00,00,00,00,00,00,00,00,66,AA,A8,AA,A8,AA,AA,A1,AA,A1,AA,A1,AA,A1,AA,A1,AA,A1,AA,A1,AA,A1,AA,A1,AA,A1,AA,A1,AA,A1,AA,A1 HEX AA,A1,AA,A1,AA,A1,AA,66,66,AA,A6,AA,AA,AA,AA,A5,AA,AA,A5,AA,AA,A5,AA,AA,A5,AA,AA,A5,AA,AA,A5,AA,AA,AA,AA,A5,AA,AA,AA,A5 HEX AA,AA,AA,AA,AA,AA,AA,66,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00 HEX 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00 HEX 66,AA,AA,AA,AA,AA,AA,AF,AA,AF,AA,AF,AA,AF,AA,AF,AA,AF,AA,AF,AA,AF,AA,AF,AA,AF,AA,AF,AA,AF,AA,AF,AA,AF,AA,AF,AA,AF,AA,66 HEX 66,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,FA,66 HEX D0,D3,A0,C1,C3,A0,C2,A0,C3,A0,A0,C4,A0,C5,A0,A0,C8,A0,CC,A0,A0,A0,D3,D0,A0,A0,A0,D0,C3,A0,A0,CF,D0,A0,C9,D4,A0,C2,CB,A0 HEX 00,00,00,00,00,00,00,00,66,AA,AA,AA,AA,AA,AA,AF,AA,AF,AA,AF,AA,AF,AA,AF,AA,AF,AA,AF,AA,AF,AA,AF,AA,AF,AA,AF,AA,AF,AA,AF HEX AA,AF,AA,AF,AA,AF,AA,66,66,FF,FF,55,FF,FF,55,FF,FF,55,55,55,FF,FF,55,FF,FF,55,FF,55,F5,5F,FF,FF,5F,FF,FF,FF,5F,FF,FF,FF HEX 5F,FF,FF,FF,5F,FF,FF,66,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00 HEX 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00 HEX 66,AA,AA,AA,AA,AA,AA,26,AA,26,AA,26,AA,26,AA,26,AA,26,AA,26,AA,26,AA,26,AA,26,AA,26,AA,26,AA,26,AA,26,AA,26,AA,26,AA,66 HEX 66,FF,55,FF,55,FF,55,FF,FF,FF,55,FF,FF,55,FF,55,FF,55,FF,55,FF,55,FF,F5,5F,F5,FF,F5,5F,F5,FF,55,FF,55,FF,55,FF,55,FF,66 HEX 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00 HEX 00,00,00,00,00,00,00,00,66,AA,AF,AF,AF,FA,AA,A5,AA,A5,AA,A5,AA,A5,AA,A5,AA,A5,AA,A5,AA,A5,AA,A5,AA,A5,AA,A5,AA,A5,AA,A5 HEX AA,A5,AA,A5,AA,A5,AA,66,66,FF,55,F5,55,FF,55,5F,5F,FF,55,FF,FF,55,F5,55,FF,55,FF,55,F5,5F,FF,55,FF,55,FF,55,FF,55,FF,55 HEX FF,55,FF,55,FF,55,FF,66,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00 HEX 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00 DISPLAYBYTES DB $00,$00,$00 ; for the "graphical" display ; PCH/REGD PCL OPCODE SWITCHBYTES DB $00,$00 SWITCHSCRATCH DB $00,$00 ; state of switches. Should all be OFF to start SIOSTATUSOUT DB $00 ; probably unnecessary SIOSTATUSIN DB $81 ; not ready for IO SIODATAOUT DB $00 ; Virtual Serial Interface, status buffer, DATA buffer SIODATAIN DB $00 ; Virtual Serial Interface, status buffer, DATA buffer SIOBUFFER DB $00 ; TEMP data SIOSTATUSBYTES DB $00,$01 ; status settings for "Have data" and "waiting data" SIO2STATUSBYTES DB $01,$02 ; has a byte, last byte sent ; SIO2 ;Receive Data Register Full (RDRF), Bit 0 - Receive Data Register Full indicates ;that received data has been transferred to the Receive Data Reqister. RDRF is ;cleared after a read of the Receive Data Register or by a maiter reset. ;The cleared or empty state indicates that the contents of the Receive Data Register are not current. ;Transmit Data Register Empty (TDRE), Bit 1 - The Transmit Data Register Empty bit being set high ;indicates that the Transmit Data Register contents have been transferred and that new data may be entered. ;The low state indicates that the register is full and that transm'ission of a new character has not begun ;since the last write data command. ; PS AC... LEGEND DB $D0,$D3,$A0,$C1,$C3,$A0,$C2 DB $A0,$C3,$A0,$A0,$C4,$A0,$C5,$A0 DB $A0,$C8,$A0,$CC,$A0,$A0,$A0,$D3 DB $D0,$A0,$A0,$A0,$D0,$C3,$A0,$A0 DB $CF,$D0,$A0,$C9,$D4,$A0,$C2,$CB ; APPLE 80...MCCREARY TITLEBAR DB $A0,$C1,$D0,$D0,$CC,$C5,$AD,$B8 DB $B0,$A0,$C3,$CF,$D0,$D9,$D2,$C9 DB $C7,$C8,$D4,$A0,$B1,$B9,$B7,$B9 DB $A0,$C2,$D9,$A0,$C4,$C1,$CE,$CE DB $A0,$CD,$C3,$C3,$D2,$C5,$C1,$D2,$00 ; "BREAKPOINTS" (BACKWARDS) BREAKPOINTS DB $D9,$D3,$D4,$CE,$C9,$CF,$D0,$CB DB $C1,$C5,$D2,$C2 BASICBUFFERLO DB BASICBUFFER BASBUFPNT DB 00 00 BASICBUFFER DB 00