From 8d6cbd2043616653cbb2e9a3399f63aa094e98ad Mon Sep 17 00:00:00 2001 From: Terence Boldt Date: Sat, 23 Oct 2021 16:39:02 -0400 Subject: [PATCH] Add input to commands --- Apple2/AT28C64B.bin | Bin 8192 -> 8192 bytes Apple2/CommandFirmware.asm | 19 +- Apple2/CommandFirmware.lst | 171 +++++++++-------- RaspberryPi/Apple2-IO-RPi.hdv | Bin 33553920 -> 33553920 bytes .../apple2driver/a2io/communication.go | 9 +- RaspberryPi/apple2driver/handlers/exec.go | 175 +++++++++++++----- 6 files changed, 245 insertions(+), 129 deletions(-) diff --git a/Apple2/AT28C64B.bin b/Apple2/AT28C64B.bin index 00bf3da5df13f9a93b64670b2e5c7ef7b73c5b53..26928c7ae65510aeadf6621b8da3470dff542de3 100644 GIT binary patch delta 1333 zcmZwBziSjx5Ww*zVA%Y)+*OiTn6Wg(#x#~Gukn9~g&0^1|AR{-BH>_RixfezP{R`5 z60rypl-XbJ8A3w36hRw{6d`gcVvumBZ)P97WqH-W%zWXqw!F5yGw=68eCK_6je~Ir z@%!;r?CA*sepu?p?_Vy#v!U?D_WOfB@EB*q!D0vFTlzycy!P++xVx;n1oyqcZMYaN zdf2%X9`ad7v*E9mCl6PjK7ZkL#X2r5?EGEr_c6X3t^0HButon@@qGUk#{X8LkN%@* zK)41&zT%q)`{%hVk+Ge|NAGix%p4O1|4vd-LMX2Q%Z6x$VZQZsX6 z=ETemGlw8|FmubK#xixs%p8*3!OWY?J;VIU%&9tamzm#~xr<`2VoqshLCk`fg<%#D z6b@!#nbcUO?wDCXQaG4JlX+m6hs>O=GZizBn5j@4RLp73EQwhXvoy>Sg3`e(Et49{ z)EzTRNJ;~(e=_p~#c9QCX{IEmB&IY>34wGlrDalMnYv@9ghV=+ zvdKI*%nN4D)R||@{LRcW6z3IlMl%&L6)}}zDhQN=sVtKk%hVk+6(q{RR88iMVGfzu Vt~0NhIb!BDiW_2n9<|3h{{iO`efaY5P;zjP%h@fly;7#g``MhnKF&7f5H0&c0n7#CJ?y7VnHkv39=Hf5D{eW zYgZ9OD+|HaLX?Q85J_-mva(lPH88_F&vt9OHCXi4JL;ftv8jf$&{5a-U;P43@YPYX z-9OxE!p2w@iZ|WiH>|0J-mpJ|XAF4fEpKu3F^z<}Dd3y8@j~x2*!3F4!6?w)YM|;6 zOuVJ_PDoNnQfQ?R{4hrftwe#7Fk&eL5oV=QqdFMLebw$ScK4JN!GpIlMN5cAghqr$ z7LCAMjYSRkCI>YZrNE&WF={}VEIQBVgwY=*fB(_5$$YW?2X{2d761SM diff --git a/Apple2/CommandFirmware.asm b/Apple2/CommandFirmware.asm index 18dd4d2..fd3c0ed 100644 --- a/Apple2/CommandFirmware.asm +++ b/Apple2/CommandFirmware.asm @@ -27,6 +27,9 @@ MenuCommand = $08 InputString = $fd67 PrintChar = $fded +Keyboard = $c000 +ClearKeyboard = $c010 +Wait = $fca8 .org SLOT*$100 + $C000 ;ID bytes for booting and drive detection @@ -79,6 +82,7 @@ GetCommand: bcc GetCommand SendCommand: + bit ClearKeyboard lda #$05 ;send command 5 = exec jsr SendByte ldy #$00 @@ -98,6 +102,12 @@ DumpOutput: cmp #$00 beq endOutput jsr PrintChar + bit Keyboard ;check for keypress + bpl DumpOutput ;keep dumping output if no keypress + lda Keyboard ;send keypress to RPi + and #$7f + jsr SendByte + bit ClearKeyboard clc bcc DumpOutput endOutput: @@ -132,7 +142,14 @@ GetByte: waitRead: lda InputFlags rol - bcs waitRead + bcc readByte + bit Keyboard ;keypress will abort waiting to read + bpl waitRead + lda #$1f ;set all flags high and exit + sta OutputFlags + lda #$ff + rts +readByte: lda InputByte pha lda #$1f ;set all flags high diff --git a/Apple2/CommandFirmware.lst b/Apple2/CommandFirmware.lst index dcf61eb..d4e2b7b 100644 --- a/Apple2/CommandFirmware.lst +++ b/Apple2/CommandFirmware.lst @@ -31,6 +31,9 @@ Current file: CommandFirmware.asm 000000r 1 000000r 1 InputString = $fd67 000000r 1 PrintChar = $fded +000000r 1 Keyboard = $c000 +000000r 1 ClearKeyboard = $c010 +000000r 1 Wait = $fca8 000000r 1 000000r 1 .org SLOT*$100 + $C000 00C700 1 ;ID bytes for booting and drive detection @@ -56,18 +59,18 @@ Current file: CommandFirmware.asm 00C719 1 Start: 00C719 1 20 00 C3 jsr $c300 ;enable 80 columns 00C71C 1 A9 05 lda #$05 ;execute command -00C71E 1 20 7D C7 jsr SendByte +00C71E 1 20 90 C7 jsr SendByte 00C721 1 A0 00 ldy #$00 00C723 1 sendHelp: -00C723 1 B9 76 C7 lda HelpCommand,y +00C723 1 B9 89 C7 lda HelpCommand,y 00C726 1 F0 06 beq endSendHelp -00C728 1 20 7D C7 jsr SendByte +00C728 1 20 90 C7 jsr SendByte 00C72B 1 C8 iny 00C72C 1 D0 F5 bne sendHelp 00C72E 1 endSendHelp: 00C72E 1 A9 00 lda #$00 -00C730 1 20 7D C7 jsr SendByte -00C733 1 20 68 C7 jsr DumpOutput +00C730 1 20 90 C7 jsr SendByte +00C733 1 20 6B C7 jsr DumpOutput 00C736 1 00C736 1 A5 33 lda $33 00C738 1 48 pha @@ -83,82 +86,88 @@ Current file: CommandFirmware.asm 00C74B 1 90 F0 bcc GetCommand 00C74D 1 00C74D 1 SendCommand: -00C74D 1 A9 05 lda #$05 ;send command 5 = exec -00C74F 1 20 7D C7 jsr SendByte -00C752 1 A0 00 ldy #$00 -00C754 1 getInput: -00C754 1 B9 00 02 lda $0200,y -00C757 1 C9 8D cmp #$8d -00C759 1 F0 08 beq sendNullTerminator -00C75B 1 29 7F and #$7f -00C75D 1 20 7D C7 jsr SendByte -00C760 1 C8 iny -00C761 1 D0 F1 bne getInput -00C763 1 sendNullTerminator: -00C763 1 A9 00 lda #$00 -00C765 1 20 7D C7 jsr SendByte -00C768 1 DumpOutput: -00C768 1 20 9B C7 jsr GetByte -00C76B 1 C9 00 cmp #$00 -00C76D 1 F0 06 beq endOutput -00C76F 1 20 ED FD jsr PrintChar -00C772 1 18 clc -00C773 1 90 F3 bcc DumpOutput -00C775 1 endOutput: -00C775 1 60 rts -00C776 1 -00C776 1 HelpCommand: -00C776 1 61 32 68 65 .byte "a2help",$00 -00C77A 1 6C 70 00 -00C77D 1 -00C77D 1 SendByte: -00C77D 1 48 pha -00C77E 1 waitWrite: -00C77E 1 AD FB C0 lda InputFlags -00C781 1 2A rol -00C782 1 2A rol -00C783 1 B0 F9 bcs waitWrite -00C785 1 68 pla -00C786 1 8D FD C0 sta OutputByte -00C789 1 A9 1E lda #$1e ; set bit 0 low to indicate write started -00C78B 1 8D F7 C0 sta OutputFlags -00C78E 1 finishWrite: -00C78E 1 AD FB C0 lda InputFlags -00C791 1 2A rol -00C792 1 2A rol -00C793 1 90 F9 bcc finishWrite -00C795 1 A9 1F lda #$1f -00C797 1 8D F7 C0 sta OutputFlags -00C79A 1 60 rts -00C79B 1 -00C79B 1 GetByte: -00C79B 1 A9 1D lda #$1d ;set read flag low -00C79D 1 8D F7 C0 sta OutputFlags -00C7A0 1 waitRead: -00C7A0 1 AD FB C0 lda InputFlags -00C7A3 1 2A rol -00C7A4 1 B0 FA bcs waitRead -00C7A6 1 AD FE C0 lda InputByte -00C7A9 1 48 pha -00C7AA 1 A9 1F lda #$1f ;set all flags high -00C7AC 1 8D F7 C0 sta OutputFlags -00C7AF 1 finishRead: -00C7AF 1 AD FB C0 lda InputFlags -00C7B2 1 2A rol -00C7B3 1 90 FA bcc finishRead -00C7B5 1 68 pla -00C7B6 1 end: -00C7B6 1 60 rts -00C7B7 1 -00C7B7 1 00 00 00 00 .repeat 251-3~lEP_%g$Y_!A(Uwg?s3@|hRJI~RMTUrgVL4D#plrliDIkQsWL0RG zS^CT+zG90cj^IV1JEeXzBX3mSV##9W=vokMaxid*q?aquP z_N}qj1bbc_OWiDWv(z12>NafdFiYK`rQC#;>dsv1HXQCSOWlQ+ZVWEXFqg(#W6Fh= zt~ZxDSZdE|ZLKzMWkhLvUOYoR4D~S76CCO>Y@RSfJ)xo8gof(Q9O^L~o-ji_g@^7A z4&7r8tzKYgra5$P{?In}hE`{&m!V#UdV@o~hRqvhs5dl}o6u0*nM1vX!y9I(xA4$| z!J&uEp*0E&4VXiZW=m~S^>HMMX?hUQUP(MTc4D|P=Dc} z=YvBpnnP<97<$eedMSTs`+GxcF*Lx?07C=8p#j4d2s1Pg8p=&*sP4?60mBgpGc-_m z=7cE}$(Gq1K)ibzqpPa>q4+%LtYgghV*N=b>7hoqE5Nogq~ z4@+4oC*`GrL`#fRlt-kJRF*1IRSc;nu@WcoQeA3DO{pcdrH<5@y+CB3DO^p)r31$j~W$xG5-2FO4eBrnTgc}0fEt1?uE$#8j1Y%)Sd$|xBvW8`&t zL*A6PWURa`@5nfLSKgENWxPy~iIOanWU@?=sWMHb%M6(*AIL2EP*UV0NtKUfwxr1% znJe?;6PYgy#4ZbEkt~)avQ(DIa#`I^d--e7s!jGA9;!NCj-bpGKjoP29sCF5b`P+ zN`{f)>$m`?{@+NtUj3sZAcgQ&ME_silgMN;g-j*W z$aFG;%p@O>S>!{KLOvp?(vfE}2I@A@j)sVkZm9BC?n)Axp_JvYf0SE6FOd zntV#ukhNqTSx+{Qbn+S5NHⓈvk#J7P6IWBiqRi@;Ui}WRfq*PO^*aCSQ>~WG~rA zoWw=k#6!HqNBkr}_LBqTYjThrB8SNl@(npkz9rw0@5wQ8ocusekRQoU!-!FS$all56BTxk3ITH_0t> So7^F}B+t4}|9;jY$NmQ?sNS{! delta 3146 zcmZwHX_U&+ zolq*;sFbBr7>SUj=egZ;W?syT&vXCh-t(L9nS0L6n1e?uj=`KpIThMBXqJ_mwJ9s` zVO-0cg)MWkw^Z+uS2#Uu(XhgzazS3<@-=%3vUm+bg)^IWD3~$6Y0&@B?5r{?CKv4u zx)xTQT2zq3#~h^JD_?(f`e@hxN8!R1S^SvohpJB9ojF& z^hzQKCrYl=b)~Z_bzMoil5{1RSxE+=WGO4j%!&uaD;%X)l0i6Ga;2UtU0kW>O3Ia# zE2+#%DhQ=YSxIG9JSbk_D7}&j!l{xg^?HV9Eamk z0Vkj$D&a(&gvvM>Rd5PUMOB=JYB(L$aR$!BSqM-AHBk$>sEs88#yz+f12G7L zF$DKvDDKBFJb>YN5F;=WqmYM(FdAbp77t?_9>I7#iV2vA$M85N;R#H}lbC|3n1-kD zG@ik;cn zgKzO2zQ=lOzz_HlKVc(&#xK}}&Desi_!Yn5cWlES_!HZ)13OWKUHA*T@i+EhFaE(k z?8gE8i-S0X!#FaDzx^pNWlULfl*uvW%+aR2ImR4ojx)!b3g!e;(Nr=gnv+arbF!&o zPBEvNs^&CP&75wkn={Or<}4GK8m6YHWpYhzQ^(Xb^-TJgIoqcOrlDzM&M}S6xu%IZ z&onjXn`WlDX<;rf7n+u)m1%7*G8daR<`Q$Mxy-aRmzyh0JJa5DFda=N)7f+}UCou| zDs#2zX1bee%(dn^)5BbEdYWFQx4FUGXl^on%+2N&bE~<{+-~}se&!C---OHnbEmn> z+->eL_nLubkQr=-nET97bH5p89x%hrgJy&oX-1hm^N<;B#+b3@VKdG=V#b?C%>*;i zJZ2s@lgtxlvU$=>F;mSn^OSkoJY$|U&zXGlym`UAXu{?t^RjuxylSSK*UanY4Ku^c zG_%Z`<}FiT-Zt-;cg<|`o_XKQF>}p4Gv6#QAD9o#Li3SXWIi^F%@Xs8S!$MFiDd#%gqY&saa`Onbqbq^SN1LzA#^!ugqGr&U|gYG2fc+%=c!!*X1_UL{xt{9 PA#*r;8UOvV%dGtmif!!% diff --git a/RaspberryPi/apple2driver/a2io/communication.go b/RaspberryPi/apple2driver/a2io/communication.go index 46d62cc..2e6abd6 100644 --- a/RaspberryPi/apple2driver/a2io/communication.go +++ b/RaspberryPi/apple2driver/a2io/communication.go @@ -81,7 +81,7 @@ func InitGpio() { out_bit1.Out(gpio.Low) out_bit0.Out(gpio.Low) - edgeTimeout = time.Second * 5 + edgeTimeout = time.Second } func ReadString() (string, error) { @@ -176,9 +176,16 @@ func ReadByte() (byte, error) { } func WriteByte(data byte) error { + // check if the Apple II wants to send a byte to us first + if in_write.Read() == gpio.Low { + out_write.Out(gpio.High) + return errors.New("Can't write byte while byte is incoming") + } + // wait for the Apple II to be ready to read for in_read.Read() == gpio.High { if !in_read.WaitForEdge(edgeTimeout) { + out_write.Out(gpio.High) return errors.New("Timed out writing byte -- read stuck high") } } diff --git a/RaspberryPi/apple2driver/handlers/exec.go b/RaspberryPi/apple2driver/handlers/exec.go index 089d65f..00bb8db 100644 --- a/RaspberryPi/apple2driver/handlers/exec.go +++ b/RaspberryPi/apple2driver/handlers/exec.go @@ -1,11 +1,12 @@ package handlers import ( + "errors" "fmt" + "io" "os" "os/exec" "strings" - "bufio" "github.com/tjboldt/Apple2-IO-RPi/RaspberryPi/apple2driver/a2io" ) @@ -36,53 +37,34 @@ func ExecCommand() { return } if linuxCommand == "a2help" { - a2io.WriteString("\r" + - "This is a pseudo shell. Each command is executed as a process. The cd command\r" + - "is intercepted and sets the working directory for the next command. Running\r" + - "commands that do not exit on their own will not be able to be cancelled\r" + - "without resetting the Apple II. For example, do not use ping without a way to\r" + - "limit output like -c 1.\r" + - "\r" + - "Built-in commands:\r" + - "a2help - display this message\r" + - "a2wifi - set up wifi\r" + - "A2LOWER - force lowercase for II+\r" + - "\r") + a2help() return } if linuxCommand == "A2LOWER" { - forceLowercase = true - a2io.WriteString("All commands will be converted to lowercase\r") + a2lower() return } if linuxCommand == "a2wifi" { - a2io.WriteString("\r" + - "Usage: a2wifi list\r" + - " a2wifi select SSID PASSWORD\r" + - "\r") + a2wifi() return } if linuxCommand == "a2wifi list" { - linuxCommand = "sudo iwlist wlan0 scanning | grep ESSID | sed s/.*ESSID://g | sed s/\\\"//g" + linuxCommand = a2wifiList() } if strings.HasPrefix(linuxCommand, "a2wifi select") { - params := strings.Fields(linuxCommand) - if len(params) != 4 { - a2io.WriteString("\rIncorrect number of parameters. Usage: a2wifi select SSID PASSWORD\r\r") - return - } - ssid := params[2] - psk := params[3] - linuxCommand = "printf \"country=ca\\nupdate_config=1\\nctrl_interface=/var/run/wpa_supplicant\\n\\nnetwork={\\n scan_ssid=1\\n ssid=\\\"%s\\\"\n psk=\\\"%s\\\"\\n}\\n\" " + - ssid + " " + - psk + " " + - " > /tmp/wpa_supplicant.conf; " + - "sudo mv /tmp/wpa_supplicant.conf /etc/wpa_supplicant/; " + - "sudo wpa_cli -i wlan0 reconfigure" + linuxCommand, err = a2wifiSelect(linuxCommand) } + if err == nil { + execCommand(linuxCommand, workingDirectory) + } +} + +func execCommand(linuxCommand string, workingDirectory string) { cmd := exec.Command("bash", "-c", linuxCommand) cmd.Dir = workingDirectory stdout, err := cmd.StdoutPipe() + stdin, err := cmd.StdinPipe() + if err != nil { fmt.Printf("Failed to set stdout\n") a2io.WriteString("Failed to set stdout\r") @@ -96,26 +78,127 @@ func ExecCommand() { return } - reader := bufio.NewReader(stdout) + outputComplete := make(chan bool) + inputComplete := make(chan bool) + userCancelled := make(chan bool) - for err == nil { - var b byte - b, err = reader.ReadByte() - if err == nil { + if linuxCommand == "openssl" { + fmt.Printf("\nSending help command...\n") + io.WriteString(stdin, "help\n") + } + + go getStdin(stdin, outputComplete, inputComplete, userCancelled) + go getStdout(stdout, outputComplete, userCancelled) + + for { + select { + case <-outputComplete: + outputComplete <- true + case <-userCancelled: + a2io.WriteString("^C\r") + cmd.Process.Kill() + return + case <-inputComplete: + cmd.Wait() + a2io.WriteByte(0) + return + default: + } + } +} + +func getStdout(stdout io.ReadCloser, done chan bool, userCancelled chan bool) { + for { + select { + case <-userCancelled: + stdout.Close() + return + default: + bb := make([]byte, 1) + n, err := stdout.Read(bb) + if err != nil || n == 0 { + stdout.Close() + done <- true + return + } + b := bb[0] fmt.Print(string(b)) if b == 10 { // convert LF to CR for Apple II compatiblity b = 13 } b |= 128 - err = a2io.WriteByte(b) - if err != nil { - fmt.Printf("\nFailed to write byte\n") - cmd.Process.Kill() - return + a2io.WriteByte(b) + } + } +} + +func getStdin(stdin io.WriteCloser, done chan bool, inputComplete chan bool, userCancelled chan bool) { + for { + select { + case <-done: + stdin.Close() + inputComplete <- true + return + default: + b, err := a2io.ReadByte() + if err == nil { + if b == 3 { + stdin.Close() + userCancelled <- true + return + } else { + if b == 13 { + b = 10 + } + fmt.Printf("%c", b) + io.WriteString(stdin, string(b)) + } } } } - - cmd.Wait() - a2io.WriteByte(0) +} + +func a2help() { + a2io.WriteString("\r" + + "This is a pseudo shell. Each command is executed as a process. The cd command\r" + + "is intercepted and sets the working directory for the next command.\r" + + "\r" + + "Built-in commands:\r" + + "a2help - display this message\r" + + "a2wifi - set up wifi\r" + + "A2LOWER - force lowercase for II+\r" + + "\r") +} + +func a2lower() { + forceLowercase = true + a2io.WriteString("All commands will be converted to lowercase\r") +} + +func a2wifi() { + a2io.WriteString("\r" + + "Usage: a2wifi list\r" + + " a2wifi select SSID PASSWORD\r" + + "\r") +} + +func a2wifiList() string { + return "sudo iwlist wlan0 scanning | grep ESSID | sed s/.*ESSID://g | sed s/\\\"//g" +} + +func a2wifiSelect(linuxCommand string) (string, error) { + params := strings.Fields(linuxCommand) + if len(params) != 4 { + a2io.WriteString("\rIncorrect number of parameters. Usage: a2wifi select SSID PASSWORD\r\r") + return "", errors.New("Incorrect number of parameters. Usage: a2wifi select SSID PASSWORD") + } + ssid := params[2] + psk := params[3] + linuxCommand = "printf \"country=ca\\nupdate_config=1\\nctrl_interface=/var/run/wpa_supplicant\\n\\nnetwork={\\n scan_ssid=1\\n ssid=\\\"%s\\\"\n psk=\\\"%s\\\"\\n}\\n\" " + + ssid + " " + + psk + " " + + " > /tmp/wpa_supplicant.conf; " + + "sudo mv /tmp/wpa_supplicant.conf /etc/wpa_supplicant/; " + + "sudo wpa_cli -i wlan0 reconfigure" + return linuxCommand, nil }