-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmsxirc.asm
7386 lines (6957 loc) · 253 KB
/
msxirc.asm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
; MSX IRC Client v 1.1
; Original Client by Pasha Zakharov
; Reviewed and fixed by Oduvaldo Pavan Junior (ducasp / [email protected])
;
; v1.0 - Original Release by Pasha Zakharov
;
; v1.1 - Release by Oduvaldo Pavan Junior
; -> Fixes to the UNAPI calling code so it works with ROM UNAPI's
; -> Fixes not being able to connect to server when using a INI file as
; input parameter
; -> Fixes DOS1 Mapper support when no mapper present
; -> DOS1 Mapper wouldn't allow usage of #FF segment for our program, it
; a valuable segment, one more window and no reason to do so
; -> Fixes possible crash with DOS1 mapper support and Mapper UNAPI, it
; was assuming it always use last mapper segment in DOS1, this is not
; a rule even though it works for Obsonet that does that :)
; -> Changed the exit function, so it closes open connection as well
; -> Updated so the main menu won't show remainings of the cursor or of
; the SIAT :)
; -> Improvement on the Help File
; -> Text strings review / correction
; -> Clean-up of code
; -> Commentaries about code functionality
;
; How this IRC Client works?
; That is what I ( Ducasp ) found out so far:
;
; This absolutely require a MSX 2 at least as it works on text mode 2.
;
; It also needs a memory mapper with at least 2 segments free, as each "window"
; (channel, system, private message) occupies a segment to buffer what has
; been received. More free segments means more windows can be open! :)
;
; It uses a custom/direct I/O to VDP after setting it to text mode 2, so all
; routines that put text on screen are customized. Those routines are driven by
; WCB's (Window Control Block), that define start VDP address, buffer address,
; etc...
;
; A WCB also tells if its contents are going to be dumped on VDP or not. If not
; , all content just goes to system / segment RAM. This allows each window to
; be updated even if not being displayed, and select which one is currently
; on screen.
;
; How the lower bar works?
;
; That is a neat idea... TEXT2 alternative mode uses 212 scan lines, which if
; you consider each character is 8 bits tall, means you have 26.5 lines...
; No kidding, so you end-up with a 27th line that only the upper portion of it
; is visible. So, it is a useless line where you can't have full characters...
; Only that Pasha had this nice idea, use it to show symbols, 80, meaning the
; 27th line will contain special characters from his customized font that are
; a small square for unused, a stilized S for the Server Window, a stilized H
; for the Help Window, a q for private messages (Query) and a C for channels.
; Also, the selected one uses the attribute of alternate color, so it is
; very nice interface. By hitting CTRL+LEFT or CTRL+RIGHT you navigate all the
; open windows.
;
; The special sauce of it is SSIAT, 80 bytes for the 27th line that is updated
; by SSIA function, also, after it, there 10 bytes, which is used to set the
; attributes of the patterns used on the 27th line, so, of those 80bits, the
; bits that are set are the ones that will show in alternate color, hilighting
; the selection.
;
; So, SSIA function uses MAPTAB, which is 80 words (160 bytes) table, where the
; LSB of the word (1st byte) is the functionality of that position ( 'S' for
; Server Window, 'H' for Help Window, 'C' for a Channel Window and finally 'Q'
; for the Private Message (Query) Window, otherwise, it is 0 meaning empty. The
; MSB of the word (2nd byte) is the segment assigned to that Window, that must
; be selected at page 2 when updating or showing it.
;
; How the segment/Window list is built?
;
; Basically list is built at 0x9000 by BFSegT, and is held in Main Menu / App
; segment memory.
; It is composed of window/segment number in HEX, then the printable window/
; segment number, and the window title. Each record is 56 bytes long. sWCB1
; 17th byte holds a count of how many windows/nicknames are listed, having a
; different behavior than other WCB's.
;
; sWCB0 -> Will hold the WCB that is currently being showed on the Screen, be
; it the Main Menu, Server Control, Query or Channel Windows.
;
; sWCB1 -> Hold the WCB for either the Window list used by Main Menu or Nick
; list used by Channel Windows.
;
; sWCB2 -> Hold the WCB for the input box in the Server Control, Channel and
; Query windows.
;--- Macro for printing a $-finished string
;--- Changes DE and C
macro print data
ld de,data
ld c,_STROUT
call DOS
endm
;--- Constants
;--- Code legibility constants
NICK_COUNT: equ 23 ; when sWCB1 is selected, this holds the nick count
WINDOW_COUNT: equ 16 ; when sWCB1 is selected, this holds the window count
WIN_LIST_BUILD_TMP: equ 17 ; when sWCB1 is selected, this holds a temporary count of scanned segment entries
WIN_LIST_ITEM_SEL: equ 10 ; when sWCB1 is selected, this holds the window list item that is currently selected
WIN_LIST_SHIFT: equ 22 ; when sWCB1 is selected, this holds how many itens should skip before start rendering the list
WIN_CR_CLR_CURS_ON: equ 20 ; If on CR will clear from current cursor position on before returning to home X position
WIN_AUTO_SCROLL: equ 9 ; If windows Auto Scrolls or not
WIN_AUTO_LF: equ 8 ; If windows Auto Line Feed or not
WIN_BUFF_STS: equ 21 ; WCB Buffer Status - 0 normal or 1 out of Buffer
WIN_H_POS: equ 6 ; H cursor position a given WCB
WIN_V_POS: equ 7 ; V cursor position a given WCB
WIN_V_SIZE: equ 5 ; maximum number of lines for a given WCB
WIN_H_SIZE: equ 4 ; maximum number of columns for a given WCB
WIN_WRITE_TO_VRAM: equ 11 ; if this WCB is being written to VRAM (showing on screen)
WIN_RAM_B_ADD: equ 12 ; Address of the RAM buffer for our window
WIN_RAM_B_ADD_LSB: equ 12 ; Address of the RAM buffer for our window, LSB
WIN_RAM_B_ADD_MSB: equ 13 ; Address of the RAM buffer for our window, MSB
WIN_RAM_B_CUR: equ 18 ; Address of the current position in RAM buffer for our window
WIN_RAM_B_CUR_LSB: equ 18 ; Address of the current position in RAM buffer for our window, LSB
WIN_RAM_B_CUR_MSB: equ 19 ; Address of the current position in RAM buffer for our window, MSB
WIN_RAM_B_END: equ 16 ; Address of the end of RAM buffer for our window
WIN_RAM_B_END_LSB: equ 16 ; Address of the end of RAM buffer for our window, LSB
WIN_RAM_B_END_MSB: equ 17 ; Address of the end of RAM buffer for our window, MSB
WIN_L_STR_ADD: equ 22 ; Address of the last string printed
WIN_L_STR_ADD_LSB: equ 22 ; Address of the last string printed, LSB
WIN_L_STR_ADD_MSB: equ 23 ; Address of the last string printed, MSB
WINDOW_VR_ADD: equ 2 ; VRAM address to start writing for this WCB
WINDOW_VR_ADD_LSB: equ 2 ; VRAM address to start writing for this WCB, LSB
WINDOW_VR_ADD_MSB: equ 3 ; VRAM address to start writing for this WCB, MSB
;--- DOS function calls
DOS: equ #0005 ; DOS Function call entry
_TERM0: equ #00 ; DOS Program terminate
_CONIN: equ #01 ; Console input with echo
_CONOUT: equ #02 ; Console output
_DIRIO: equ #06 ; Direct console I/O
_INNO: equ #07 ; Direct console I/O, but locks waiting for input
_INNOE: equ #08 ; Console input without echo
_STROUT: equ #09 ; String output
_BUFIN: equ #0A ; Buffered line input
_CONST: equ #0B ; Console status
_FOPEN: equ #0F ; Open file
_FCLOSE equ #10 ; Close file
_SDMA: equ #1A ; Set Disk Transfer Address
_RBREAD: equ #27 ; Random block read
_TERM: equ #62 ; Terminate with error code
_DEFAB: equ #63 ; Define abort exit routine
_DOSVER: equ #6F ; Get DOS version
_GETDATE: equ #2A ; Get Date
_GETTIME: equ #2C ; Get Time
;--- Will mess with AF in addition to any registers of BIOS function being called
macro bios bioscall
rst #30 ; Interslot call
db 0 ; Slot 0
dw bioscall ; Function
endm
;--- BIOS functions calls
RDSLT: equ #000C ; Reads value of an address in another slot
CALSLT: equ #001C ; Interslot Call
ENASLT: equ #0024 ; Switch a given page to a given slot
WRTVDP: equ #0047 ; Write data in VDP Register C=R, B=Data
RDVRM: equ #004A ; Reads from VRAM
WRTVRM: equ #004D ; Writes in VRAM
FILVRM: equ #0056 ; Fill VRAM with a value
LDIRVM: equ #005C ; Block Transfer RAM -> VRAM
INITXT: equ #006C ; Initialize Screen 0
CHGET: equ #009F ; One character input (blocking)
INLIN: equ #00B1 ; Store keys in buffer until STOP or ENTER is pressed
BREAKX: equ #00B7 ; Test status of CTRL+STOP
BEEP: equ #00C0 ; BEEP BEEP
CLS: equ #00C3 ; Clear screen
POSIT: equ #00C6 ; Move cursor to specified position
GTSTCK: equ #00D5 ; Returns joystick status
;--- MSX System Variables
TPASLOT1: equ #F342 ; Slot Address of RAM in page 1 (DOS Only)
linnl40: equ #F3AE ; Screen Width of Screen 0
ARG: equ #F847 ; Argument for MATHPACK, also used by UNAPI
timer: equ #FC9E ; JIFFY, increases every VDP VSYNC interrupt
EXTBIO: equ #FFCA ; EXTBIO Hook, to access EXTBIO functions like UNAPI
;--- Scan code special buttons
ESC_: equ 27
UP_: equ 30
DOWN_: equ 31
LEFT_: equ 29
RIGHT_: equ 28
TAB_: equ 9
BS_: equ 8
SELECT_: equ 24
CLS_: equ 11
INC_: equ 18
DEL_: equ 127
;--- TCP/IP UNAPI routines
TCPIP_GET_CAPAB: equ 1
TCPIP_DNS_Q: equ 6
TCPIP_DNS_S: equ 7
TCPIP_TCP_OPEN: equ 13
TCPIP_TCP_CLOSE: equ 14
TCPIP_TCP_ABORT: equ 15
TCPIP_TCP_STATE: equ 16
TCPIP_TCP_SEND: equ 17
TCPIP_TCP_RCV: equ 18
TCPIP_WAIT: equ 29
;--- TCP/IP UNAPI error codes
ERR_OK: equ 0
ERR_NOT_IMP: equ 1
ERR_NO_NETWORK: equ 2
ERR_NO_DATA: equ 3
ERR_INV_PARAM: equ 4
ERR_QUERY_EXISTS: equ 5
ERR_INV_IP: equ 6
ERR_NO_DNS: equ 7
ERR_DNS: equ 8
ERR_NO_FREE_CONN: equ 9
ERR_CONN_EXISTS: equ 10
ERR_NO_CONN: equ 11
ERR_CONN_STATE: equ 12
ERR_BUFFER: equ 13
ERR_LARGE_DGRAM: equ 14
ERR_INV_OPER: equ 15
;*** MAIN PROGRAM ***
; MSX-DOS start's *.com files at #100
org #100
ld hl,(chsas) ; Load Reference Value from memory
ld de,1255 ; This is what the value should be
xor a ; Clear flags
sbc hl,de ; Compare
jr z,MSXIRCST0
; If not zero, corrupt, so exit
print CORRUPTFILE_S ; Ok, tell corrupt file
ret
MSXIRCST0:
ld a,#00 ; slot bios 0
ld hl,#002D ; Check MSX Version
bios RDSLT ; Read from bios
or a
jr nz,MSXIRCST1
; If zero, MSX1, need MSX2 at least
print MSX1_S ; Ok, tell it is MSX1 and need MSX2
ret
MSXIRCST1:
;--- Checks the DOS version and establishes variable DOS2
ld c,_DOSVER
call DOS ; Get DOS version
or a
jr nz,NODOS2 ; NZ on A means it is not MSX DOS
ld a,b
cp 2
jr c,NODOS2 ; B<2 means it is older than MSX DOS 2
ld a,#FF
ld (DOS2),a ; #FF for DOS 2, 0 for DOS 1
print USEDOS2_S ; Ok, tell we recognize we are under DOS2
NODOS2:
;--- Prints the presentation
print PRESENT_S
print INFO_S
ld a,(DOS2)
or a
jp nz,LsSPD2 ; If we've detected DOS2, use DOS2 routines
;--- DOS1 not mapper support
call INIMAPDOS1 ; And try to initialize it
ld a,(freemaps) ; How many free segments?
cp 2
jr c,NOMAPPER ; Yeah, we need at least 2 free segments to be useful, one for server and one for a chat window
jr MAPPERDONE ; Skip dos 2 routines and go on
LsSPD2:
;--- Get mapper support routines in DOS 2
ld de,#0402 ; Device 4 is DOS2 Mapper, Function 2 is get mapper jump table
xor a
call EXTBIO ; Call it, A will hold number of segments of main mapper, B the slot of it, C how many free segments we have in it and HL the jump table address
or a
jp nz,MAPPER2 ; If has any segment, follow through
NOMAPPER:
; Ok
print SM_NOMAPPER
ret ; No mapper, no play
MAPPER2:
ld (totmaps),a ; Save total segments
ld a,c
ld (freemaps),a ; Save free segments
cp 2
jr c,NOMAPPER ; Yeah, we need at least 2 free segments to be useful, one for server and one for a chat window
ld de,ALL_SEG
ld bc,16*3
ldir ; Copy the jump table to ALL_SEG
MAPPERDONE:
;--- Time to Check UNAPI / TCPIP
;--- Search a TCP/IP UNAPI implementation
ld hl,TCPIP_S ; API specification identifier (TCP/IP)
ld de,ARG ; Loaded in F847
ld bc,15 ; Copy 15 bytes
ldir
;--- Check how many TCP/IP implementation are available
xor a ; A = 0
ld b,a ; B = 0
ld de,#2222 ; UNAPI EXTBIO CALL
call EXTBIO
ld a,b ; B has number of implementations found
or a ; 0 ?
jp z,IUNAPID ; And done looking for UNAPI
;--- Ok, there is at least one, so let's get the first one
ld a,1 ; 1-st API (identifier = TCP/IP)
ld de,#2222 ; UNAPI EXTBIO CALL, get details of 1-st Implementation
call EXTBIO ;A=slot , B=segment (#FF=not RAM), HL=enter if #C000-FFFF
; page - 3 then A,B register not use
;--- Setup the UNAPI calling code
ld (CALL_um+8),hl ; At this point we don't know if it is page 3, mapper or
ld (CALL_U+5),hl ; rom, so save it for both mapper and rom routines
ld c,a ; Save slot in C
ld a,h
cp #C0 ; Is address in page 3?
ld a,c ; Slot back in A
jr c,NO_UNAPI_P3 ; If not in page 3, interslot call or ramhelpr call
;--- Page 3 UNAPI
ld a,#C3
ld (CALL_U),a ; (JP XXXX (hl))
ld (CALL_U+1),hl ; Page 3 is just a direct call, and its return return direct to the caller
ld hl,SM_UNAPI3
jr OK_SET_UNAPI ; And that is all we need
NO_UNAPI_P3:
;--- Ok, it is either a mapper or rom UNAPI
ld (CALL_U+2),a ; Slot UNAPI ROM implementation, just in case
ld a,b
cp #FF ; If segment is FF, it means ROM
jr nz,NO_UNAPI_ROM ; So if not FF, let's prepare CALL_U for mapper routines
jr OK_SET_UNAPI ; And done
NO_UNAPI_ROM:
;--- UNAPI is in ram segment, so we need to update CALL_um that is the call for memory mapper
ld (CALL_um+2),a ; Mapper segment that UNAPI is installed
ld a,(TPASEG1) ; Our current segment, to switch back after calling UNAPI
ld (CALL_um+12),a ; Put into CALL_um
; Ok, the mapper segment needs to be reserved if in DOS1, otherwise it will be overwritten
ld a,(DOS2)
or a
jp NOT_DOS1 ; If not on dos 1, no need to care
; Ok, DOS1, let's reserve the segment used by UNAPI
ld a,(CALL_um+2) ; Mapper segment for UNAPI
ld e,a ; Save segment number to allocate in E
and %00000111 ; Remainder of division per 8, basically the bit number in the MAP tap
ld b,a ; Save in B
xor a ; A and Carry 0
ld d,a ; D = 0
ld a,e ; Segment number in A
rra
rra
rra
and %00011111 ; Divide by 8, so this is the MAPTAB index
ld e,a ; Save MAPTAB index in E, D is o
ld hl,EMAPTAB
add hl,de ; HL now has the MAPTAB for byte for this segment
xor a
scf ; Carry 1, A = 0, our OR mask to set used segment
inc b ; For bit 0, need to do it once, bit 2? three times... So inc counter
D1RAMUNAPIALLOC:
rla
djnz D1RAMUNAPIALLOC ; A will have the bit masked (0)
or (hl)
ld (hl),a ; Mask the MAP tab occupying the segment
ld a,(freemaps) ; Get free segments
dec a
ld (freemaps),a ; Save free segments
cp 2 ; How many free segments?
jp c,NOMAPPER ; Yeah, we need at least 2 free segments to be useful, one for server and one for a chat window, less than that don't even bother
NOT_DOS1:
ld hl,CALL_um
ld de,CALL_U
ld bc,18
ldir ; Overwrite CALL_U with CALL_um, and we are ready to work with MM UNAPI
jp OK_SET_UNAPI ; And done
IUNAPID:
;--- If here, no TCPIP UNAPI has been found
print NOTCPIP_S ; No TCP/IP found message
ret
OK_SET_UNAPI:
;--- Checks if there are command line parameters.
; If not, try to use default ( MSXIRC.INI )
ld a,1
ld de,BUFFER ; Our 512 bytes buffer
call EXTPAR ; Get 1st parameter, it must be ini file name
jr c,NOPARA ; If no parameter, keep going with the default
; convert filename.ext to FILENAMEEXT 8+3
ld hl,BUFFER
ld DE,FCB+1
call CFILENAME ; Will convert, if need, from "name.ext" to NAME EXT [8+3]
NOPARA:
;--- Open file .ini, set user parameter's
ld de,FCB
ld c,_FOPEN
call DOS ; Filename in FCB
or a
ld de,NOINIF_S
jp nz,PRINT_TERML ; If error opening, print error message and terminate
ld de,BUFFER
ld c,_SDMA
call DOS ; Our 512 bytes buffer is now being used to store file transfers
ld hl,1
ld (FCB+14),hl ; set record size = 1 byte
ld de,FCB
ld hl,512 ; read 512 records
ld c,_RBREAD
call DOS
ld (LBUFF),hl ; Number of bytes read in LBUFF
ld de,BUFFER
add hl,de ; HL has end of buffer
xor a
ld (hl),a ; 0 -> end record with null termination
ld de,FCB
ld c,_FCLOSE
call DOS ; Close the file, bye bye and thanks for all the fish!
;--- Extract user parameters
ld bc,(LBUFF)
ld a,b
or c
jp z,CHECKSL ; If LBUFF is zero, no records
ld hl,BUFFER
ld iy,WRDPA
call G_PA ; Parse and extract parameters
ld a,(PA_ST)
and %01000000 ; Do we have a font to load?
call nz,LOADFONT ; If yes, load it
;--- Apply color and timestamp parameters from .INI (or use the default ones in RAM)
call APP_PA
CHECKSL:
;--- If we are in DOS 2, set the abort exit routine
ld a,(DOS2)
or a
ld de,EXIT ; From now on, pressing CTRL-C
ld c,_DEFAB ; has the same effect of Q in the main menu
call nz,DOS ; (aborts the TCP connection and terminates program)
ei
ld b,60
Lsw:
halt
djnz Lsw ; Wait 1s...
call INISCREEN ; Initialize screen amd clear tables as needed
;--- 1st screen ( info )
;--- clear screen
call CLS_G ; Initialize / Clear Screen Buffers
call LOAD_S ; And load screen buffer in VRAM
; initialize system window in RAM
ld hl,WCB4 ; Parameters for system Window
ld de,sWCB0 ; Go to Window 0
ld bc,24
ldir
; initialize select page window in RAM
ld hl,WCB5 ; Parameters for page select that goes in system Window
ld de,sWCB1 ; Go to Window 1
ld bc,24
ldir
; position BIOS cursor
ld hl,#0019 ; v-25
bios POSIT
; print system information
ld ix,sWCB0
ld bc,0
ld (sWCB0+6),bc ; set cursor at 0x0
ld hl,SYSMESS1
call PRINT_TW ; And print our welcome message in system window
ld a,(PA_ST)
and %01000000
jr z,Ls01 ; Using a custom font?
ld a,(fntsour)
or a
jr z,Ls02 ; Yes, but was it loaded?
ld hl,SM_fntLOAD
call PRINT_TW ; Yes, print a message about it then...
jp LsSP1 ; And keep going
Ls02:
ld hl,SM_fntLERR
call PRINT_TW ; Font not loaded, so, error message is printed
ld a,(fferr)
add a,"0"
call OUTC_TW ; And the error code
ld a,13
call OUTC_TW
ld a,10
call OUTC_TW ; And jump line
Ls01:
ld hl,SM_fntBIOS
call PRINT_TW ; Tell we are using the bios font
LsSP1:
ld hl,SM_D2MAPI
ld a,(DOS2)
or a
jp nz,LsSPD1.2 ; If we've detected DOS2, using DOS 2 routines
;--- DOS1 not mapper support
ld hl,SM_DOS1MAP
LsSPD1.2:
call PRINT_TW ; Tell the type of mapper routines being used
ld hl,SM_D2M_TOT
call PRINT_TW ; And let's detail total segments
ld d,0
ld a,(totmaps)
ld e,a
or a ; If A = 0 and we are here, it is dos 1 with a 4MB Mapper
jr nz,NOT4MONDOS1
ld de,#100 ; if here 256 segments available
NOT4MONDOS1:
ld hl,BUFFER
ld b,3
ld c," "
ld a,%00001000
call NUMTOASC
call PRINT_TW ; Total number of segments on screen
ld a,13
call OUTC_TW
ld a,10
call OUTC_TW ; Jump line
ld hl,SM_D2M_FREE
call PRINT_TW ; Now free segments
ld d,0
ld a,(freemaps)
ld e,a
ld hl,BUFFER
ld b,3
ld c," "
ld a,%00001000
call NUMTOASC
call PRINT_TW ; Number of free segments on screen
ld a,13
call OUTC_TW
ld a,10
call OUTC_TW ; Jump Line
call GET_P2 ; Check the segment on page 2 (0x8000-0xBFFF)
ld (P2_sys),a ; And save it in our variable
call GET_P1 ; Get segment for page 1 (0x4000-0x7FFF)
ld (TPASEG1),a ; Save it in TPASEG1 to make sure it is restored
ld ix,sWCB0 ; System Message Window
ld hl,SM_BASICHLP
call PRINT_TW ; Print a basic help message
;***************************************************************
; Root enter main program
; System info, select work segment's open/close segment,s
; SYS_S is a point to always go back to the previous segment
; When starting it just select segment 0, which I believe is
; the System Message Window
;***************************************************************
SYS_S:
call CSIA ; Make sure we do not get attribute on the lower 27th line
ld a,(segp) ; The segment that was executing (first time will be 0)
ld (segsel),a ; And make it selected
SYS_S1:
ld ix,sWCB0 ; Main Screen Window
ld a,(P2_sys) ; The Segment of page 2 of our application
call PUT_P2 ; Make sure it is paged
call BFSegT ; Buffer Segments Information
call BOSegT ; Move it to the screen buffer
call LOAD_S ; And update screen from buffer
; draw attribute cursor segment select (ix+WIN_LIST_ITEM_SEL) 1..24 (0-off)
ld ix,sWCB1 ; Secondary (Selection) Window
call CLAT_C_N ; Clear the attributes of the secondary window (either a nick list or a window list go there)
call SETAT_S ; Generate the alternate color for what is selected
call LOAD_SA ; And now send that to the VDP
LsSPW:
call TCPSEP ; Check if there is data from server, if there is a connection
call CLOCK ; Print date and time on top right corner of the screen
ld c,_CONST
call DOS ; Check if key was pressed
or a
jr z,LsSPW ; If no key pressed, just loop
; key was pressed
ld c,_INNO
call DOS ; Get key
ld b,a ; and move key to B
ld ix,sWCB0 ; Main Window WCB in IX
ld hl,#FBEB
ld a,b
bit 5,(hl) ; Was F1 pressed?
jp z,Ls_help ; Go to Help Instance
cp #0D ; Enter?
jp z,Ls_GoTo ; Go to the selected Window
cp LEFT_
jp z,LsDEC ; If left decrease selection of selected window
cp UP_
jp z,LsDEC ; Same for Up
cp DOWN_
jp z,LsINC ; If down increase selection of selected window
cp RIGHT_
jp z,LsINC ; Same for right
cp 27
jp z,Ls_ESC ; If ESC try to go back to previous window
and %01011111 ; Uppercase
cp "S"
jp z,SERV_C ; If S or s, server Window
cp "Q"
jp z,EXIT ; If Q or q, exit routine
jp LsSPW ; Otherwise, loop
;--- Go to the segment / window on our left side
S_LEFT:
call CURSOFF
ld a,(segp)
ld b,a ; Save current segment in B
S_L2:
dec b
ld a,#FF
cp b
jr nz,S_L3 ; Safe guard, if segment greater than 0 ok, but if 0, the left will wrap-around to the 80th segment, segment 79
ld b,79
S_L3:
ld a,b ; Segment - 1 in A
rla ; Multiply it by 2, MAPTAB is comprised of pairs
ld e,a ; E has the segment ID * 2
ld d,0 ; and D must b 0, 79*2 < 0xFF
ld hl,MAPTAB
add hl,DE ; Get the MAPTAB entry
ld a,(hl)
or a ; Is it an existing segment?
jr z,S_L2 ; nope, decrease and try again
;--- Found a segment to our left, could be our own...
ld a,b
ld (segsel),a ; Save as the segment selected
jp Ls_GoTo ; And go to it
;--- Go to the segment / window on our right side
S_RIGHT:
call CURSOFF
ld a,(segp)
ld b,a ; Save current segment in B
S_R2:
inc b
ld a,79
cp b
jr nc,S_R3 ; Safe guard, if segment lower than 80 ok, but if 80, the right will wrap-around to the 1st segment, segment 0
ld b,0
S_R3:
ld a,b ; Segment + 1 in A
rla ; Multiply it by 2, MAPTAB is comprised of pairs
ld e,a ; E has the segment ID * 2
ld d,0 ; and D must b 0, 79*2 < 0xFF
ld hl,MAPTAB
add hl,DE ; Get the MAPTAB entry
ld a,(hl)
or a ; Is it an existing segment?
jr z,S_R2 ; nope, increase and try again
;--- Found a segment to our right, could be our own...
ld a,b
ld (segsel),a ; Save as the segment selected
;--- And go to it
;--- DO NOT ADD FUNCTIONS HERE, or, if you do, need to add a jp Ls_Goto above
; Go to on selected segment (channel/Query/Server/Help)
Ls_GoTo:
call CLAT_C_N ; Clear the attributes of the secondary window (either a nick list or a window list go there)
call LOAD_SA ; And send color attributes of secondary window to the VDP
xor a ; Clear flags
ld a,(segsel) ; Segment selected in A
Ls_go1:
rla ; Multiply by 2
ld e,a
ld d,0 ; DE has MAPTAB offset
ld hl,MAPTAB
add hl,DE ; Position of our segment in MAPTAB
ld a,(hl) ; Now let's identify it
and %01111111
cp "H"
jp z,LsEnSH ; Prepare for Help Menu and then enter its loop
cp "C"
jp z,LsEnCS ; Prepare for Channel and then enter its loop
cp "S"
jp z,LsEnSS ; Prepare for Server Window and then enter its loop
cp "Q"
jp z,LsEnQS ; Prepare for Query (private message) Window and then enter its loop
ld a,(P2_sys)
call PUT_P2 ; None, so back to our main menu, allocate app P2
; ld hl,SM_LostSeg
; call PRINT_TW ; The only time I saw this happen is if you hit enter without anything selected, so just ditch the message, not meaningful
jp LsSPW ; Main system menu loop
;--- Decrease selection on auxiliary window
LsDEC:
ld ix,sWCB1
ld a,(ix+WIN_LIST_ITEM_SEL) ; Current selection
cp 1
jr z,LsD1 ; If one, do not decrease
dec a
ld (ix+WIN_LIST_ITEM_SEL),a ; Decrement it and save
jp LsD2
LsD1:
; Current selection was 1
ld a,(ix+WIN_LIST_SHIFT) ; Is listing shifted?
or a
jp z,LsSPW ; If zero, not shifted, main system menu loop
; Yes, it is shifted
dec a
ld (ix+WIN_LIST_SHIFT),a ; Decrement shift and save
LsD2:
ld a,(ix+WIN_LIST_ITEM_SEL) ; selection
add a,(ix+WIN_LIST_SHIFT) ; add shift
ld hl,#9000 ; pointer to buffer
ld de,50+4+1 ; Window Information Buffered size
LsD4:
dec a ; Decrement
jr z,LsD3 ; if 0, done
add hl,de ; Next window information
jr LsD4 ; loop until HL has the address of the first position to start our list
LsD3:
ld a,(hl)
ld (segsel),a ; The segment selected segment is saved
jp SYS_S1 ; Back to redraw main system menu and then loop
;--- Increase selection on auxiliary window
LsINC:
ld ix,sWCB1
ld b,(ix+WIN_LIST_ITEM_SEL) ; Current selection
ld c,(ix+WIN_LIST_SHIFT) ; List shift
ld a,b
add a,c ; add both to get the current item
cp (ix+WINDOW_COUNT)
jp nc,LsSPW ; if >=, no redraw, already in the last, just back to menu loop
ld a,b
cp (ix+WIN_V_SIZE) ; check if need to scroll
jr nc,LsI1 ; If at last line, yeah, scroll
; No need to scroll
inc a ; Just add 1 to the item selection
ld (ix+WIN_LIST_ITEM_SEL),a ; Save it
jr LsD2 ; Remaining is done in LsD2
LsI1:
inc c
ld (ix+WIN_LIST_SHIFT),c ; Ok, item selected will still be the last in the list, but now increase the list shift... :)
jr LsD2 ; Remaining is done in LsD2
; Go back to last window
Ls_ESC:
call CLAT_C_N ; Clear the attributes of the secondary window (either a nick list or a window list go there)
call LOAD_SA ; And send color attributes of secondary window to the VDP
xor a ; Clear flags
ld a,(segp) ; and we want to go to the segment that was being displayed before entering the main menu
jp Ls_go1 ; there we go
LsEnSH:
;************************************
; Enter Help Segment
;************************************
inc hl ; Jump to the mapper segment
ld a,(hl) ; now in A
ld (S_C),a ; And this is the main window segment
ld a,(segsel)
ld (segp),a ; segment selected saved in segp
jp Hmcw ; And help menu loop
Ls_help:
;*************************************
; Select Help Segment (Open if needed)
;*************************************
call CURSOFF
; clear kbd buffer (Fx Key) and Attributes of Nick/List area as Help occupies all screen
call CLKB
call CLAT_C_N
; Find help segment
ld a,"H"
ld de,helpdes ; Help Segment descriptor
call SrS ; Check if it already exists
jr c,Ls_h_noh ; If carry, nope
ld (S_C),a ; Otherwise, it is selected and it is "Server Control Window"
jp Hmcw ; And help menu loop
; Help segment not found, initialize help segment
Ls_h_noh:
; get free record
call ALL_REC
ld a,b
ld (segp),a ; This will be the current segment/window being displayed
jp c,NO_REC ; If carry, no more records!
push hl ; Save the MAP Table entry
ld a,0
ld b,a ; Primary mapper only
call ALL_SEG ; Request a user segment to be allocated from primary maper
jp c,NO_SEG ; If carry, failed to allocate
pop hl ; Restore MAP Table entry
; set record to H status
ld (hl),"H"
inc hl
; set segment mapper
ld (hl),a
; initialize Help Segment
ld (S_C),a ; Load it in screen control segment
call PUT_P2 ; Select the segment in page 2
call CLS_G ; Clear the screen buffer as we are going to build the screen for our new segment
ld a,(segp)
ld (segs),a ; make it the selected segment
ld d,0
ld a,(segs)
ld e,a ; DE has segment #
ld hl,#8000
ld b,2
ld c,"0"
ld a,%00000000
call NUMTOASC ; Will convert the segment number in printable format
ld hl,#8000+3
ld (hl),"H" ; H identify Window as H type
inc hl
inc hl
ld de,helpdes ; Help Window descriptor
ex de,hl
ld bc,4
ldir
ld hl,WCB0 ; help WCB template
ld de,sWCB0 ; will go to the main Window
ld bc,24
ldir ; set WCB to segment
ld ix,sWCB0
; load help file
ld hl,FCBhelp+1+8+3 ; point past FCB filename
ld b,28
xor a
LsHi2:
ld (hl),a
inc hl
djnz LsHi2 ; and reset FCB
ld a,(P2_sys)
call PUT_P2 ; Put our APP segment back in P2
ld de,FCBhelp
ld c,_FOPEN
call DOS ; open the file
ld de,#9000
ld c,_SDMA
call DOS ; transfer area at 0x9000
ld hl,1
ld (FCBhelp+14),hl
ld de,FCBhelp
ld hl,#BFFF-#9000 ; Read all we can in the buffer reserved area
ld c,_RBREAD
call DOS ; Execute read
ld (var2),hl ; Store Help Size
ld de,FCBhelp
ld c,_FCLOSE
call DOS ; Close file
; copy help buffer to help segment
ld hl,#9000 ; Origin
LsHi1:
ld a,(P2_sys)
call PUT_P2 ; Main App segment
ld a,(hl)
ld b,a ; Copy help byte to B
ld a,(S_C)
call PUT_P2 ; Help segment
ld a,b
ld (hl),a ; Save it
inc hl ; Adjust pointr
ld a,h
cp #C0
jr nz,LsHi1 ; And copy all the way up to C0000.... Doesn't seem smart, we have size in var2, why not use it? TODO
; set size of help bufer
ld ix,sWCB0 ; Help segment WCB
ld hl,(var2) ; Size of help in HL
ld de,(sWCB0+12) ; #9000 text buffer
add hl,de
ld (sWCB0+16),hl ; end buffer
call BFSegT ; rebuild active segment table as it sits on 0x9000 and was destroyed :)
jp Hmcw ; Help screen operations
;--- Get the Segment for Server Control in P2
Hmcw:
ld a,(S_C)
call PUT_P2 ; Help segment @ P2
; set segment information attributes
call SSIA
ld ix,sWCB0 ; WCB where Help is
call CLS_TW ; Initialize screen buffer for it
ld hl,(sWCB0+WIN_RAM_B_CUR) ; RAM Buffer for Help in HL
xor a
ld (ix+WIN_H_POS),a
ld (ix+WIN_V_POS),a ; Cursor @ 0x0
ld (ix+WIN_BUFF_STS),a ; And buffer normal
inc a
Hmcw2:
ld a,(ix+WIN_V_POS) ; v pos (line)
ld c,(ix+WIN_V_SIZE) ; v max
cp c ; at the last line
jp p,Hmcw1 ; y posit > max y (out of screen)
ld de,(sWCB0+WIN_RAM_B_END) ; RAM Buffer End in DE
ld a,l
sub e
ld a,h
sbc a,d ; HL - DE should carry, otherwise done
jr nc,Hmcw3 ; out of buffer to print
ld a,(hl) ; Get char
inc hl ; update pointer
cp #0A
jr nz,Hmcw0 ; If new line jump
ld (sWCB0+WIN_L_STR_ADD),hl ; Update last string address
Hmcw0:
push hl ; Save HL
call OUTC_TW ; Char on screen
pop hl ; Restore HL
jp Hmcw2 ; And loop
Hmcw3:
ld a,1
ld (ix+WIN_BUFF_STS),a ; out of buffer to print
Hmcw1:
call LOAD_S ; And update screen with WCB buffer
; operations
HmwcG:
call CLOCK ; Update clock and time on screen
call TCPSEP ; Check if there is data, if connection is open of course
jp SEL_RE ; Safe guard, just in case segment is lost, switch to different window, otherwise, will end-up in HLP_RE
HLP_RE:
call L_SIA ; Update the last line with the segment information
ld c,_CONST
call DOS
or a
jr z,HmwcG ; If zero, no key pressed, loop
; Key pressed, let's get it
ld c,_INNO
call DOS
ld b,a ; Key IN B
ld a,(S_C)
call PUT_P2 ; Make sure Help segment is @P2
ld a,b ; Key back in A
ld hl,#FBEB ; 6th keyboard matrix row address
ld ix,sWCB0
cp ESC_
jp z,Hmcw_EX ; Return to main screen
cp UP_
jr z,HmcwUP ; Check if scroll one line or page up
cp DOWN_
jr z,HmcwDOWN ; Check if scroll one line or page down
bit 1,(hl)
jr z,HmcWCTRL ; If CTRL is pressed, check more stuff
jp Hmcw ; Otherwise back to main loop
; Check if anything is pressed with CTRL that we care about
HmcWCTRL:
cp LEFT_
jp z,S_LEFT ; CTRL Left -> Switch to the window to the left of ours, if any
cp RIGHT_
jp z,S_RIGHT ; CTRL Right -> Switch to the window to the right of ours, if any
and %01011111
cp 17 ; CTRL+Q
jp z,HmcwCLOSE ; Close this Window
jp Hmcw ; Otherwise, back to main loop
HmcwUP:
ld b,1
ld hl,#FBEB
bit 0,(hl) ; Shift pressed?
jr nz,Hmcw_UP2 ; If not, will go one line up
ld b,(ix+WIN_V_SIZE) ; If pressed, go up the number of lines (a page)
Hmcw_UP2:
ld hl,(sWCB0+WIN_RAM_B_CUR)
ld de,(sWCB0+WIN_RAM_B_ADD)
dec hl
Hmcw_UP1:
xor a
push hl ; Save HL
sbc hl,de ; CUR - ADD
pop hl ; Restore HL
jp m,Hmcw_UP3 ; If CUR<ADD go to UP3, nothing else to do / buffer finished
; CUR >= ADD
dec hl
ld a,(hl)
cp #0A
jr nz,Hmcw_UP1 ; Loop until no more data in buffer or new line
dec b
jr nz,Hmcw_UP1 ; And loop until no more lines to go up
Hmcw_UP3:
inc hl
ld (sWCB0+WIN_RAM_B_CUR),hl ; Update Current
jp Hmcw ; Back to help main loop
HmcwDOWN:
ld a,(ix+WIN_BUFF_STS)
or a
jp nz,Hmcw ; Back to help main loop if out of buffer to print
ld hl,#FBEB
bit 0,(hl) ; Shift pressed?
jr nz,Hmcw_DW0 ; If not, will go one line down
; Go up to one page down
ld a,(ix+WIN_L_STR_ADD_LSB)
ld (ix+WIN_RAM_B_CUR_LSB),a
ld a,(ix+WIN_L_STR_ADD_MSB)
ld (ix+WIN_RAM_B_CUR_LSB),a ; Last string in current, so start drawing from the last
jp Hmcw ; Back to help main loop
; One line down
Hmcw_DW0:
ld hl,(sWCB0+WIN_RAM_B_CUR) ; Current in HL
Hmcw_DW1:
ld a,#0A
ld bc,0
Hmcw_DW2:
cpir ; Search for Line Feed
jp nz,Hmcw_DW2 ; Repeat if not found
ld (sWCB0+WIN_RAM_B_CUR),hl ; Save as last string
jp Hmcw ; Back to help main loop
;--- Exit Help Menu
Hmcw_EX: